将 C# ConcurrentBag<T> 与多个生产者和一个消费者一起使用
Using C# ConcurrentBag<T> with multiple producers and a single consumer
我遇到这样一种情况,多个线程正在创建一个 ICollection 对象。 ConcurrentBag 似乎是最好的(?)解决方案,因为 - 1)每个线程都有自己的本地队列,以及 2)线程不需要通信 - 它们是独立的。到目前为止一切顺利,但事实是我需要从这个方法 return 一个 ISet(在所有生产者都终止之后)。即使当前的 ConcurrentBag 实例 IS 不同(由于应用程序的逻辑,它是有保证的),我仍然需要将它转换为 ISet,例如 HashSet。在这一点上没有更多的生产者。现在是真正的问题:
当迭代 ConcurrentBag 时,调用线程是否会为不在线程本地队列中的 each 项获取锁?或者它会为每个线程获取一次锁吗?此外,仅迭代包之间的内部实现与显式调用 bag.Distinct() 之间是否存在差异,锁定?
查看 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
方法,因此没有区别。
我遇到这样一种情况,多个线程正在创建一个 ICollection 对象。 ConcurrentBag 似乎是最好的(?)解决方案,因为 - 1)每个线程都有自己的本地队列,以及 2)线程不需要通信 - 它们是独立的。到目前为止一切顺利,但事实是我需要从这个方法 return 一个 ISet(在所有生产者都终止之后)。即使当前的 ConcurrentBag 实例 IS 不同(由于应用程序的逻辑,它是有保证的),我仍然需要将它转换为 ISet,例如 HashSet。在这一点上没有更多的生产者。现在是真正的问题:
当迭代 ConcurrentBag 时,调用线程是否会为不在线程本地队列中的 each 项获取锁?或者它会为每个线程获取一次锁吗?此外,仅迭代包之间的内部实现与显式调用 bag.Distinct() 之间是否存在差异,锁定?
查看 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
方法,因此没有区别。