Использование C# ConcurrentBag с несколькими производителями и одним потребителем
У меня есть ситуация, когда несколько потоков создают один объект ICollection. ConcurrentBag кажется лучшим (?) решение, так как-1) каждый поток будет иметь свою собственную локальную очередь, и 2) потокам не нужно общаться - они независимы. Пока все хорошо, но правда в том, что мне нужно вернуть Исет из этого метода (после того, как все производители прекратили). Даже если текущий экземпляр ConcurrentBag отличается от (это гарантируется логикой приложения) , Мне все еще нужно преобразовать его в ISet, скажем, HashSet. В этом пункте больше нет производителей. А теперь возникает реальный вопрос:
Когда ConcurrentBag повторяется, будет ли вызывающий поток захватывать блокировку для каждого элемента, который не находится в локальной очереди потока? Или он захватит замок один раз за нить? Кроме того, есть ли разница во внутренней реализации между простой итерацией мешка - и между вызовом мешка.Distinct() явно, по замку?
1 ответ:
Взглянув на исходный код
ConcurrentBag: http://referencesource.microsoft.com/#System/sys/system/collections/concurrent/ConcurrentBag.cs,537a65e966c1c38dПеребирание пакетов вызывает вызов
FreezeBag. Этот метод вызываетAcquireAllLocks, которые просматривают очередь каждого потока и устанавливают блокировку:/// <summary> /// local helper method to acquire all local lists locks /// </summary> private void AcquireAllLocks() { Contract.Assert(Monitor.IsEntered(GlobalListsLock)); bool lockTaken = false; ThreadLocalList currentList = m_headList; while (currentList != null) { // Try/Finally bllock to avoid thread aport between acquiring the lock and setting the taken flag try { Monitor.Enter(currentList, ref lockTaken); } finally { if (lockTaken) { currentList.m_lockTaken = true; lockTaken = false; } } currentList = currentList.m_nextList; } }Он захватит блокировку один раз за нить, а не один раз за элемент.
Итерация или вызов
Distinctоба вызовут методGetEnumerator, поэтому он не делает разница.
Comments