2014-12-19 4 views
6

Для примера следующего кода поточно:Являются ли операции linq в параллельных коллекциях потоками безопасными?

ConcurrentQueue<Guid> _queue = new ConcurrentQueue<Guid>(); 
     while(true) 
     { 
      for(int y = 0; y < 3; y++) 
      { 
       if(y % 3 == 0) 
       { 
        System.Threading.Tasks.Task.Run(() => _queue.Enqueue(Guid.NewGuid())); 
       } 
       else if (y % 3 == 1) 
       { 
        Guid x; 
        System.Threading.Tasks.Task.Run(() => _queue.TryDequeue(out x)); 
       } 
       else if(y % 3 == 2) 
       { 
        System.Threading.Tasks.Task.Run(() => 
        { 
         if (_queue.Any(t => t == testGuid)) 
         { 
          // Do something 
         } 
        }); 

       } 
      } 

Edit: Видимо название не было достаточно ясно, так обновили пример кода, чтобы включить фактическое многопоточное поведение, да код выше просто образца многопоточного поведения.

+3

Где находится нить? – leppie

+0

У вас может быть это в приложении asp.net, и несколько потоков будут обращаться к нему, если вы не создадите их вручную. – pollirrata

+1

В вашем коде нет многопоточности, но если вы находитесь в многопоточной среде, ConcurrentQueue определенно является хорошим типом для использования, поскольку он обеспечивает безопасный доступ к вашей очереди. – FloChanz

ответ

8

Операции LINQ доступны только для чтения, поэтому они являются потокобезопасными на все коллекции. Конечно, если вы добавите код, который изменяет коллекцию внутри метода Where или Select, они перестают быть потокобезопасными.

Нитевидные коллекции гарантируют, что модификации являются потокобезопасными, что на самом деле не является проблемой при выполнении запроса LINQ.

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

+0

Спасибо, это то, чем я был после. Поэтому, если я хочу, чтобы операции LINQ и изменения коллекции были взаимоисключающими, я должен реализовать свою собственную коллекцию блокировки. – AncientSyntax

+0

Это зависит от многого. Это может быть полезно, например, для того, чтобы обернуть очередь в сборке блокировки и выставить dequeueing как перечислимую. Затем вы можете выполнять операции linq, но только блокировать, когда очередь пуста. Или вообще не блокировать, а делать что-то еще, а потом возвращаться и повторять операции linq. Это будет одновременно изменять и делать вещи linq без блокировки и с полной безопасностью потока. –

+0

Таким образом, это означает, что блокировка необходима, чтобы избежать того, что моментальный снимок содержит устаревшие данные в более длинном запросе LINQ? В противном случае вы по-прежнему уязвимы для многопоточности. –

2

Да, согласно documentation

The System.Collections.Concurrent именам предоставляет несколько поточно-классов коллекций, которые должны использоваться вместо соответствующих типов в System.Collections и системы. Collections.Generic namespaces, когда несколько потоков равны , которые одновременно получают доступ к коллекции.

+1

[Эта документация] (http://msdn.microsoft.com/en-us/library/dd287144%28v=vs.110%29.aspx) была бы более полезной, поскольку она конкретно говорит о вызове метода в коллекции, который LINQ будет использовать. – Rawling

3

Да, но ...

Давайте возьмем ваш пример:

if(_queue.Any(t => t == testGuid)) 
{ 
    // Do something 
} 

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

Это, как таковой, поточно-безопасный.

Теперь что?

Ваш код в // Do something предположительно должен быть выполнен только в том случае, если в очереди есть элемент, который соответствует testGuid. К сожалению, мы не знаем, верно это или нет, потому что поток времени в Гераклине двинулся дальше, и все, что мы знаем, это то, что там был такого руководства.

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

Но если // Do something зависит от наличия в очереди очереди testGuid, и очередь выгружается из очереди, то код-блок в целом не является потокобезопасным, хотя выражение ссылки.

Смежные вопросы