Почему в .NET нет общей синхронизированной очереди? - PullRequest
35 голосов
/ 20 сентября 2008

Я заметил, что вы можете вызвать Queue.Synchronize, чтобы получить потокобезопасный объект очереди, но тот же метод недоступен в очереди . Кто-нибудь знает почему? Кажется странным.

Ответы [ 4 ]

53 голосов
/ 20 сентября 2008

Обновление - в .NET 4 теперь есть ConcurrentQueue<T> в System.Collections.Concurrent, как описано здесь http://msdn.microsoft.com/en-us/library/dd267265.aspx. Интересно отметить, что его метод IsSynchronized (справедливо) возвращает ложь.

ConcurrentQueue<T> - это полное перезапись с нуля, создание копий очереди для перечисления и использование передовых методов без блокировки, таких как Interlocked.CompareExchange() и Thread.SpinWait().

Остальная часть этого ответа по-прежнему актуальна, поскольку она связана с гибелью старых членов Synchronize () и SyncRoot и почему они не очень хорошо работают с точки зрения API.


Согласно комментарию Zooba, команда BCL решила, что слишком много разработчиков неправильно понимают цель Synchronize (и в меньшей степени SyncRoot)

Брайан Грюкмейер описал это в блоге команды BCL пару лет назад: http://blogs.msdn.com/bclteam/archive/2005/03/15/396399.aspx

Ключевой проблемой является получение правильной детализации вокруг блокировок, когда некоторые разработчики наивно используют несколько свойств или методов в «синхронизированной» коллекции и полагают, что их код является поточно-ориентированным. Брайан использует Queue в качестве своего примера,

if (queue.Count > 0) {
    object obj = null;
    try {
        obj = queue.Dequeue();

Разработчики не осознают, что Count может быть изменен другим потоком до вызова Dequeue.

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

Как упоминает Брайан, удаление SyncRoot было отчасти потому, что он был в основном введен для поддержки Synchronized, а также потому, что во многих случаях лучше выбирать объект блокировки - в большинстве случаев, либо сам экземпляр Queue, либо

private static object lockObjForQueueOperations = new object();

в классе, владеющем экземпляром очереди ...

Этот последний подход обычно наиболее безопасен, поскольку позволяет избежать некоторых других распространенных ловушек:

Как говорится, многопоточность трудна , и упрощать ее может быть опасно.

7 голосов
/ 20 сентября 2008

Возможно, вам стоит проверить Parallel CTP; вот запись в блоге от ребят, которые ее собирают, это довольно актуально:

Перечисление параллельных коллекций

Это не совсем то же самое, но это может решить вашу большую проблему. (Они даже используют Queue<T> против ConcurrentQueue<T> в качестве примера.)

6 голосов
/ 28 марта 2011

Сейчас есть, в .Net 4.0:

ConcurrentQueue<T> 

в System.Collections.Concurrent

http://msdn.microsoft.com/en-us/library/dd267265.aspx

0 голосов
/ 20 сентября 2008

(я полагаю, вы имеете в виду очередь для второго.)

Я не могу конкретно ответить на вопрос, за исключением того, что свойства IsSynchronized и SyncRoot (но явно не Synchronize ()) наследуются от интерфейса ICollection. Ни одна из общих коллекций не использует это, и интерфейс ICollection не включает SyncRoot.

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

...