Подтвердить что ты не робот

Являются ли IEnumerable методы Linq потокобезопасными?

Интересно, являются ли методы расширения Linq атомарными? Или мне нужно lock любой объект IEnumerable, используемый для потоков, перед любой итерацией?

Влияет ли объявление переменной volatile на это?

Подводя итог, какое из следующего является лучшим, потокобезопасным, операция?

1- Без каких-либо блокировок:

IEnumerable<T> _objs = //...
var foo = _objs.FirstOrDefault(t => // some condition

2- Включение операторов блокировки:

IEnumerable<T> _objs = //...
lock(_objs)
{
    var foo = _objs.FirstOrDefault(t => // some condition
}

3 Объявление переменной как изменчивой:

volatile IEnumerable<T> _objs = //...
var foo = _objs.FirstOrDefault(t => // some condition
4b9b3361

Ответ 1

Интерфейс IEnumerable<T> не является потокобезопасным. См. Документацию по http://msdn.microsoft.com/en-us/library/s793z9y2.aspx, которая гласит:

Перечислитель остается в силе, пока сбор остается неизменным. Если в коллекцию внесены изменения, такие как добавление, изменение или удаление элементов, перечислитель безвозвратно аннулирован и его поведение undefined.

Перечислитель не имеет эксклюзивного доступа к коллекции; поэтому перечисление через коллекцию по существу не является поточно-безопасной процедурой. Чтобы гарантировать безопасность потока во время перечисления, вы можете заблокировать сбор во время всего перечисления. Чтобы обеспечить доступ к коллекции несколькими потоками для чтения и записи, вы должны реализовать свою собственную синхронизацию.

Linq не меняет ничего из этого.

Блокировка, очевидно, может быть использована для синхронизации доступа к объектам. Вы должны заблокировать объект повсюду, к которому вы обращаетесь, хотя и не только при итерации по нему.

Объявление коллекции как изменчивой не будет иметь положительного эффекта. Это только приводит к барьеру памяти перед чтением и после записи ссылки на коллекцию. Он не синхронизирует чтение или запись коллекции.

Ответ 2

Короче говоря, они не являются потокобезопасными, как указано выше.

Однако это не означает, что вы должны блокировать до "всякой итерации".

Вам необходимо синхронизировать все операции, которые изменяют коллекцию (добавлять, изменять или удалять элементы) с помощью других операций, которые (добавление, изменение, удаление элементов или чтение элементов).

Если вы одновременно выполняете операции чтения в коллекции, блокировка не требуется. (так что выполняются команды LINQ, такие как Average, Contains, ElementAtOrDefault, все будет в порядке)

Если элементы в коллекции имеют длину машинного слова, например Int на большинстве 32-разрядных компьютеров, то изменение значения этого элемента уже выполняется атомарно. В этом случае не добавляйте и не удаляйте элементы из коллекции без блокировки, но изменение значений может быть в порядке, если вы можете справиться с некоторым недетерминированностью в своем дизайне.

Наконец, вы можете рассмотреть мелкозернистую блокировку отдельных элементов или разделов коллекции, а не блокировать всю коллекцию.