.NET - Безопасен ли поток метода Queue.Enqueue? - PullRequest
5 голосов
/ 02 ноября 2009

Допустим, у меня есть модуль с очередью.

Для постановки в очередь других сущностей они должны пройти функцию:

public sub InsertIntoQueue(Obj)
    MyQueue.Enqueue(Obj)
end sub

Если у меня запущено несколько потоков и они хотят вызвать InsertIntoQueue (), считается ли это потокобезопасным?

У меня сложилось впечатление, что в памяти есть только одна копия инструкций, необходимая для выполнения функции InsertIntoQueue () ... что заставляет меня думать, что это потокобезопасно.

Однако мне интересно, что происходит, когда два потока пытаются запустить функцию одновременно?

Безопасен ли этот поток, и если нет, как я могу сделать его безопасным? (и каковы будут последствия для производительности, касающиеся скорости и использования памяти)

Ответы [ 6 ]

8 голосов
/ 02 ноября 2009

Использовать Queue.Synchronized Обертка.

4 голосов
/ 02 ноября 2009

Нет, это не потокобезопасно.

Открытые статические (Shared в Visual Basic) члены этого типа безопасны для многопоточных операций. Члены экземпляра не гарантируют поточнобезопасность.

С сайта MSDN .

Я бы предложил добавить объект для представления дескриптора синхронизации к вашему объекту

Dim SyncHandle as Object = new Object()

И измените свой метод как таковой

Public Sub InsertIntoQueue(Object item)
    SyncLock SyncHandle 
       MyQueue.Enqueue(item)
    End SyncLock
End Sub
2 голосов
/ 02 ноября 2009

до

SyncLock MyQueue
   MyQueue.Enqueue(Obj)
End SyncLock

End Sub

1 голос
/ 02 ноября 2009

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

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

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

0 голосов
/ 03 ноября 2009

Я не эксперт по безопасности потоков, но я пытаюсь узнать как можно больше об этом.

Раньше я думал (как и вы), что эта операция может быть безопасна для потоков, так как просто записывает данные в очереди в разных потоках, и никакая очередь не выполняется. Но, как кто-то объяснил здесь (и везде в документации MSDN):

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

Это означает, что, возможно, внутренне MyQueue.Enqueue(Obj) делается так:

  1. Поместить данные в очередь ();
  2. Указатель очереди дополнения;

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

Имея это в виду, у вас есть несколько вариантов, как указано здесь, блокировка метода Enqueue (), использование Queue.Synchronized () или, может быть, даже проще, но с большим влиянием на производительность, блокировка частного свойства при доступе к объекту Queue таким образом (потому что все, что вы делаете с очередью, не будет потокобезопасным):

Private ReadOnly Property MyQueue() as Queue
Get
    SyncLock (m_myQueueLock)
        Return m_myQueue
    EndSyncLock
End Get
End Property

Надеюсь, это поможет!

0 голосов
/ 03 ноября 2009

Во избежание незначительного отклонения от темы ОП следует рассмотреть другие методы очереди. Я предполагаю, что есть по крайней мере один поток, снимающий очереди с объектов, и, возможно, вы также проверяете, является ли очередь пустой?

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

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

Это и это - интересные статьи о безопасности потоков, а также я могу рекомендовать прочитать это и / или это , в то время как они не все на vb.net, они подробно объясняют потоки.

...