При выполнении синхронизации потоков в C# должен ли я также блокировать объект, когда я читаю значение или просто меняю его?
например, у меня есть объект Queue <T>. Должен ли я просто заблокировать его при выполнении постановки в очередь и удаление, или я должен также заблокировать его при проверке таких значений, как счетчик?





Зависит от того, что вы хотите сделать с блокировкой. Обычно для такого типа блокировки требуется механизм блокировки считывающего / записывающего устройства.
Блокировка читателей / писателей означает, что читатели разделяют блокировку, поэтому вы можете иметь несколько читателей, читающих коллекцию одновременно, но для записи вы должны получить монопольную блокировку.
CLR гарантирует атомарное чтение значений вплоть до ширины процессора. Так что, если вы работаете на 32-битной системе, чтение int будет атомарным. Если вы работаете на 64-битной машине, чтение long будет атомарным. Ergo, если Count - это Int32, блокировка не требуется.
Эта почта имеет отношение к вашему вопросу.
А Count, скорее всего, является свойством, которое соответствует неявному вызову функции.
Подсчет вычислений? Из собственности? Это серьезно нарушило бы мои ожидания относительно поведения собственности. Ожидание (которое в данном случае верно) состоит в том, что доступ к свойству просто возвращает значение поля.
По крайней мере, для List <T> счетчик рассчитывается при каждом доступе! Возможно Reflector раскроет подробности о Queue <T> ...
Если вы не заблокируете его, вы можете получить более старое значение. Может возникнуть состояние гонки, при котором выполняется операция записи с изменением Count, но при изменении вы получите значение перед. Например, если в очереди только один элемент, и поток вызывает удаление из очереди, другой поток может прочитать счетчик, найти его по-прежнему равным 1 и снова вызвать удаление из очереди. Второй вызов не будет выполнен до тех пор, пока не будет предоставлена блокировка, но в это время очередь фактически будет пустой.
Из MSDN:
A Queue<(Of <(T>)>) can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.
Вы должны убедиться, что ни один читатель не активен, пока элемент находится в очереди (блокировка, вероятно, является хорошей идеей).
Глядя на счетчик в отражателе, можно увидеть чтение из частного поля. Это может быть нормально, в зависимости от того, что вы делаете со значением. Это означает, что вам не следует делать такие вещи (без надлежащей блокировки):
if (queue.Count > 0)
queue.Dequeue();
Это сработает, однако, если вы не разделяете блокировку для всех чтений, вы получите низкую производительность, поскольку только один поток может перечислить коллекцию одновременно.
Чтение int может быть атомарным, но вычисление счетчика не обязательно может быть атомарным. Это не просто чтение int. Помимо прочего, для этого требуется вызов функции.