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

StackOverflow https://stackoverflow.com/questions/107117

Вопрос

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

Это было полезно?

Решение

Обновлять - в .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();

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

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

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

private static object lockObjForQueueOperations = new object();

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

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

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

Другие советы

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

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

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

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

ConcurrentQueue<T> 

в System.Collections.Concurrent

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top