.Net BlockingCollection不能与Parallel.foreach一起使用

时间:2017-07-19 15:00:20

标签: c# multithreading parallel.foreach blockingcollection

我尝试使用BlockingCollection的值

来提供字典
(...)

lock (blockingCol)
{
    lock (cache)
    {
        cache = new Dictionary<Guid, customDocument>();

        Parallel.ForEach(blockingCol,
            (document) =>
            {
                lock (cache) //Same bug with or without this lock
                    cache.Add(document.Id, document);
            });

        blockingCol = new BlockingCollection<customDocument>();

        (...)
    }

     (...)
}

(...)

问题是有时文档已经在cache中。但我不明白为什么。我之前创建了一个新的cacheBlockingCollection不应该将同一个文档两次出列。但我认为这是Parallel的一个问题,因为当我使用它下面的代码工作时,我需要一个多线程系统。

(...)

lock (blockingCol)
{
    lock (cache)
    {
        cache = new Dictionary<Guid, customDocument>();

        while (blockingCol.Count > 0)
        {
            var doc = _blockingCol.Take();
            cache.Add(doc.Id, doc);
        }

        blockingCol = new BlockingCollection<customDocument>();

        (...)
    }

    (...)
}

(...)

1 个答案:

答案 0 :(得分:0)

问题既不是BlockingCollection,也不是Parallel.ForEach()调用,也不是锁定,尽管你可以通过正确使用并发数据结构来摆脱显式锁定。事实上,你使用的是普通词典&lt;&gt;在多线程代码中。

将您的代码段重写为

    Parallel.ForEach(blockingCol.GetConsumingEnumerable(),
        (document) =>
        {
             cache.AddOrUpdate(document.Id, document, (k,v) => document);
        });

它将消除异常ArgumentException,消息'字典中已存在具有相同键的元素。'

虽然这可以解决您的直接问题,但您应该完全重写该部分

cache = blockingCol.ToDictionary( d => d.Id );

Parallel.ForEach()在性能方面不会给你带来任何好处。

另外,请检查您的制作人,确保它没有放置两个具有相同ID的文档。