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

Почему в .NET нет единой синхронной очереди?

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

4b9b3361

Ответ 1

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

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();

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

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

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

private static object lockObjForQueueOperations = new object();

в классе, которому принадлежит экземпляр очереди...

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

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

Ответ 2

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

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

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

Ответ 4

(Я предполагаю, что вы имеете в виду Queue <T> для второго).

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

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