Использование C# ConcurrentBag с несколькими производителями и одним потребителем



У меня есть ситуация, когда несколько потоков создают один объект ICollection. ConcurrentBag кажется лучшим (?) решение, так как-1) каждый поток будет иметь свою собственную локальную очередь, и 2) потокам не нужно общаться - они независимы. Пока все хорошо, но правда в том, что мне нужно вернуть Исет из этого метода (после того, как все производители прекратили). Даже если текущий экземпляр ConcurrentBag отличается от (это гарантируется логикой приложения) , Мне все еще нужно преобразовать его в ISet, скажем, HashSet. В этом пункте больше нет производителей. А теперь возникает реальный вопрос:



Когда ConcurrentBag повторяется, будет ли вызывающий поток захватывать блокировку для каждого элемента, который не находится в локальной очереди потока? Или он захватит замок один раз за нить? Кроме того, есть ли разница во внутренней реализации между простой итерацией мешка - и между вызовом мешка.Distinct() явно, по замку?

647   1  

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

    Ничего не найдено.