2013-03-20 98 views
36

我已经阅读过以前的关于ConcurrentBag的问题,但没有在多线程中找到实际的实现示例。ConcurrentBag的正确用法是什么?

ConcurrentBag是线程安全的袋的实施,对场景中相同的线程将是既生产和消费存储在包的数据进行了优化。”

目前,这是在我的代码当前使用(这被简化而不是实际的代码):

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 
} 

我的问题:
这是ConcurrentBag正确使用它是确定在此使用ConcurrentBag种情景?

对我来说,我认为简单List<Product>和手动锁定会做得更好。原因是上述情况已经打破了“相同的线程将生产和消费存储在包”规则中的数据。
另外我还发现在并行的每个线程中创建的ThreadLocal存储在操作后仍然存在(即使线程被重用是正确的?),这可能会导致不希望的内存泄漏。
我在这一个家伙吗?或者一个简单的清除或空的方法去除ConcurrentBag中的物品就够了?

+0

我认为在这种情况下开销和整体性能会比使用同步方法更大。你测过它了吗? – 2013-03-20 10:58:58

+1

ConcurrentBag有一个构造函数,它需要一个'IEnumerable ''var myBag = new ConcurrentBag (products);' – Romoku 2013-03-20 11:55:44

+0

嗨llya,我的意思是我仍然使用上面几乎相同的代码,除了我将替换ConcurrentBag 与一个列表并添加一个锁,当在Parallel.ForEach内列表中添加一个产品 – hisoka21 2013-03-20 13:28:27

回答

13

这看起来像是使用ConcurrentBag。线程局部变量是包的成员,并且会同时包含垃圾回收(清除内容不会释放它们)。你是对的,带锁的简单列表就足以满足你的情况。如果您在循环中所做的工作非常重要,则线程同步的类型对整体性能无关紧要。在这种情况下,你可能更习惯使用你熟悉的东西。

另一种选择是使用ParallelEnumerable.Select,它与您正在尝试做的更加密切相符。同样,你将看到的任何性能差异都可能忽略不计,坚持你所知道的是没有错的。

一如既往,如果这一点的表现很关键,那么尝试它并进行测量是不可替代的。

0

在我看来,bmm6o的不正确。 ConcurrentBag实例内部包含用于向其添加项目的每个线程的迷你包,因此项目插入不涉及任何线程锁定,因此所有线程都可能进入全速运行状态而不会停滞不前并且没有任何线程上下文切换。线程同步可能需要迭代收集的项目时,但在原始示例中,迭代是在所有插入完成后由单个线程完成的。而且,如果ConcurrentBag使用互锁技术作为线程同步的第一层,则根本不可能涉及监视操作。另一方面,使用通常的List<T>实例并使用lock关键字对其每个Add()方法调用进行封装将会严重影响性能。首先,由于每个调用需要深入到内核模式并使用Windows同步基元,因此需要不断调用Monitor.Enter()Monitor.Exit()调用。其次,偶尔有一个线程可能会被第二个线程阻塞,因为第二个线程尚未完成添加。

对于我来说,上面的代码是ConcurrentBag类正确使用的一个很好的例子。