C# ConcurrentBag 的正确用法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15521584/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the correct usage of ConcurrentBag?
提问by hisoka21
I've already read previous questions here about ConcurrentBag
but did not find an actual sample of implementation in multi-threading.
我已经在这里阅读了以前的问题,ConcurrentBag
但没有找到多线程中的实际实现示例。
ConcurrentBag is a thread-safe bag implementation, optimized for scenarios where the same thread will be both producing and consuming data stored in the bag."
ConcurrentBag 是一个线程安全的包实现,针对同一个线程将生成和使用存储在包中的数据的场景进行了优化。”
Currently this is the current usage in my code (this is simplified not actual codes):
目前这是我代码中的当前用法(这是简化而不是实际代码):
private void MyMethod()
{
List<Product> products = GetAllProducts(); // Get list of products
ConcurrentBag<Product> myBag = new ConcurrentBag<Product>();
//products were simply added here in the ConcurrentBag to simplify the code
//actual code process each product before adding in the bag
Parallel.ForEach(
products,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
product => myBag.Add(product));
ProcessBag(myBag); // method to process each items in the concurrentbag
}
My questions:
Is this the right usage of ConcurrentBag
? Is it ok to use ConcurrentBag
in this kind of scenario?
我的问题:
这是正确的用法ConcurrentBag
吗?它是确定使用ConcurrentBag
在这种情况下呢?
For me I think a simple List<Product>
and a manual lock will do better. The reason for this is that the scenario above already breaks the "same thread will be both producing and consuming data stored in the bag" rule.
Also I also found out that the ThreadLocal
storage created in each thread in the parallel will still exist after the operation (even if the thread is reused is this right?) which may cause an undesired memory leak.
Am I right in this one guys? Or a simple clear or empty method to remove the items in the ConcurrentBag
is enough?
对我来说,我认为简单List<Product>
的手动锁会更好。这样做的原因是上面的场景已经打破了“同一个线程将同时生产和消费存储在包中的数据”规则。
我还发现ThreadLocal
并行中每个线程中创建的存储在操作后仍然存在(即使线程被重用是这样吗?)这可能会导致意外的内存泄漏。
我是对的吗?或者一个简单的 clear 或 empty 方法来删除中的项目ConcurrentBag
就足够了?
采纳答案by bmm6o
This looks like an ok use of ConcurrentBag. The thread local variables are members of the bag, and will become eligible for garbage collection at the same time the bag is (clearing the contents won't release them). You are right that a simple List with a lock would suffice for your case. If the work you are doing in the loop is at all significant, the type of thread synchronization won't matter much to the overall performance. In that case, you might be more comfortable using what you are familiar with.
这看起来是 ConcurrentBag 的一个不错的用法。线程局部变量是包的成员,并且在包的同时将有资格进行垃圾收集(清除内容不会释放它们)。您是对的,一个带锁的简单 List 就足以满足您的需求。如果您在循环中所做的工作非常重要,则线程同步的类型对整体性能没有太大影响。在这种情况下,您可能更愿意使用您熟悉的内容。
Another option would be to use ParallelEnumerable.Select, which matches what you are trying to do more closely. Again, any performance difference you are going to see is likely going to be negligible and there's nothing wrong with sticking with what you know.
另一种选择是使用ParallelEnumerable.Select,它与您想要做的更接近。同样,您将看到的任何性能差异都可以忽略不计,坚持使用您所知道的并没有错。
As always, if the performance of this is critical there's no substitute for trying it and measuring.
与往常一样,如果它的性能至关重要,那么尝试和测量是无可替代的。
回答by Sergey Volodko
It seems to me that bmm6o's is not correct. The ConcurrentBag
instance internally contains mini-bags for each thread that adds items to it, so item insertion does not involve any thread locks, and thus all Environment.ProcessorCount
threads may get into full swing without being stuck waiting and without any thread context switches. A thread sinchronization may require when iterating over the collected items, but again in the original example the iteration is done by a single thread after all insertions are done. Moreover, if the ConcurrentBag
uses Interlocked techniques as the first layer of the thread synchronization, then it is possible not to involve Monitor operations at all.
在我看来 bmm6o 是不正确的。该ConcurrentBag
实例内部包含每个向其添加项目的线程的迷你包,因此项目插入不涉及任何线程锁,因此所有Environment.ProcessorCount
线程都可以进入全速运行,而不会卡在等待和任何线程上下文切换。在迭代收集的项目时可能需要线程同步,但在原始示例中,在所有插入完成后,迭代由单个线程完成。而且,如果ConcurrentBag
使用Interlocked 技术作为线程同步的第一层,那么完全不涉及Monitor 操作是可能的。
On the other hand, using a usual List<T>
instance and wrapping each its Add() method call with a lock keyword will hurt the performance a lot. First, due to the constant Monitor.Enter()
and Monitor.Exit()
calls that each require to step deep into the kernel mode and to work with Windows synchronization primitives. Secondly, sometimes occasionally one thread may be blocked by the second thread because the second thread has not finished its addition yet.
另一方面,使用通常的List<T>
实例并使用 lock 关键字包装每个其 Add() 方法调用将大大损害性能。首先,由于每个需要深入内核模式并使用 Windows 同步原语的常量Monitor.Enter()
和Monitor.Exit()
调用。其次,有时一个线程可能会被第二个线程阻塞,因为第二个线程还没有完成添加。
As for me, the code above is a really good example of the right usage of ConcurrentBag
class.
对我来说,上面的代码是正确使用ConcurrentBag
类的一个很好的例子。
回答by Adeel Meer
If List<T>
is used with a lock around Add()
method it will make threads wait and will reduce the performance gain of using Parallel.ForEach()
如果List<T>
与锁定Add()
方法一起使用,它将使线程等待并降低使用的性能增益Parallel.ForEach()