Решение 11 распространенных проблем в многопоточном коде - Забытая синхронизация
ОГЛАВЛЕНИЕ
Забытая синхронизация
Это простейшая форма состязания за данные: синхронизация забыта начисто. Состязания такого рода могут быть в редких случаях щадящими, что означает их корректность, но большинство представляют собой фундаментальные проблемы с корректностью.
Проблемы вроде этой не всегда настолько очевидны. Например, объект может быть частью какого-либо большого, сложного графа объектов, который может быть достигнут из статической переменной или стать общим путем передачи объекта как части замыкания при создании нового потока или установки задания на очередь в пул потоков.
Важно обращать внимание на то, когда объект (граф) становится из закрытого общим. Это называется публикацией, и о ней мы поговорим ниже, когда речь идет об изоляции. Обратный процесс именуется приватизацией – когда объект (граф) вновь становится закрытым.
Решение – добавить адекватную синхронизацию. В примере со счетчиком я могу использовать простой захват с помощью Interlocked:
static class Counter {
internal static volatile int s_curr = 0;
internal static int GetNext() {
return Interlocked.Increment(ref s_curr);
}
}
Это работает потому, что обновление сводится к единственному месту в памяти и потому, что (к счастью) существует машинная команда (LOCK INC), эквивалентная оператору программы, который я пытаюсь сделать атомарным.
Как вариант, я могу использовать полноценную блокировку:
static class Counter {
internal static int s_curr = 0;
private static object s_currLock = new object();
internal static int GetNext() {
lock (s_currLock) {
return s_curr++;
}
}
}
Оператор блокировки гарантирует взаимное исключение между всеми потоками, пытающимися получить доступ к GetNext, и использует класс CLR System.Threading.Monitor. В программах на C++ для тех же целей используется CRITICAL_SECTION. Устанавливать блокировку в данном конкретном примере нет необходимости, но когда речь идет о нескольких операциях, их нечасто возможно свести в единую захватываемую операцию.