使用C#ConcurrentBag <t>与多个生产者和一个消费者

时间:2015-06-16 11:47:47

标签: c# .net multithreading concurrency locking

我遇到多个线程正在创建一个ICollection对象的情况。 ConcurrentBag似乎是最好的(?)解决方案,因为 - 1)每个线程都拥有它自己的本地队列,2)线程不需要通信 - 它们是独立的。到目前为止这么好,但事实是我需要从这种方法返回一个ISet(在所有生产者终止之后)。即使当前的ConcurrentBag实例 IS 不同(由于应用程序的逻辑而保证),我仍然需要将其转换为ISet,比如HashSet。在这一点上,没有更多的生产者。现在出现了一个真正的问题:

当迭代ConcurrentBag时,调用线程是否会锁定不在线程本地队列中的每个项?或者它会为每个线程抓一次锁吗?另外,在迭代行李之间的内部实现是否存在差异 - 以及在明确地调用bag.Distinct()与锁定之间是否存在差异?

1 个答案:

答案 0 :(得分:4)

查看ConcurrentBaghttp://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方法,因此它没有任何区别。