Parallel.Foreach()没有结果

时间:2018-11-16 06:08:50

标签: c# asynchronous async-await parallel.foreach

我正在尝试使用Parallel.Foreach()查询mongo-db并行性,但是没有得到任何结果。但是,当我尝试在常规的foreach循环中运行同一件事时,我能够执行预期的任务。

var exceptions = new ConcurrentQueue<Exception>();
var secondaryObjectsDictionaryCollection = new Dictionary<string, List<JObject>>();

// This works
foreach(var info in infos)
{
    try
    {
        name = await commonValidator.ValidateAsync(name);
        await commonValidator.ValidateIdAsync(name, id);
        var list = await helper.ListRelatedObjectsAsync(name, id, info, false);

        secondaryObjectsDictionaryCollection.Add(info.PrimaryId, secondaryObjectsList.ToList());
    }
    catch (Exception ex)
    {
        exceptions.Enqueue(ex);
    }
}

//This does not
Parallel.ForEach(infos, async info =>
{
    try
    {
        name = await commonValidator.ValidateAsync(name);
        await commonValidator.ValidateIdAsync(name, id);
        var list = await helper.ListRelatedObjectsAsync(name, id, info, false);

        secondaryObjectsDictionaryCollection.Add(info.PrimaryId, secondaryObjectsList.ToList());
    }
    catch (Exception ex)
    {
        exceptions.Enqueue(ex);
    }
});

我只想并行执行此任务,因为涉及到不同的mongodb集合,而且还减少了响应时间。

我无法弄清楚并行循环中出了什么问题。 并行执行这些任务的任何其他方法也将起作用。

2 个答案:

答案 0 :(得分:4)

让我们看一下说明相同问题的更简单的示例

您有与此类似的代码

var results = new Dictionary<int, int>();

Parallel.ForEach(Enumerable.Range(0, 5), async index =>
{
  var result = await DoAsyncJob(index);
  results.TryAdd(index, result);
});

您的代码因为表达式而无法运行

async index => {...}

返回未等待

的任务

像这样

Parallel.ForEach(Enumerable.Range(0, 5), index => new Task());

顺便说一句,当您像示例中那样使用多线程时,在进行并行更新以避免错误和死锁时应使用 ConcurrentDictionary 而不是Dictionary >

此处的最佳解决方案不是使用并行循环,而是使用 Task.WhenAll

var tasks = Enumerable.Range(0, 5).Select(async index =>
{
  var result = await DoAsyncJob(index);
  results.TryAdd(index, result);
});

await Task.WhenAll(tasks);

答案 1 :(得分:3)

Parallel.ForEach与传递async方法不兼容。如果您想要类似于Parallel.ForEach的东西,则可以使用Dataflow,它是ActionBlock。

var workerBlock = new ActionBlock<Info>(async info => 
{
    try
    {
        name = await commonValidator.ValidateAsync(name);
        await commonValidator.ValidateIdAsync(name, id);
        var list = await helper.ListRelatedObjectsAsync(name, id, info, false);

        //Note this is not thread safe and you need to put a lock around it.
        lock (secondaryObjectsDictionaryCollection) 
        {
            secondaryObjectsDictionaryCollection.Add(info.PrimaryId, secondaryObjectsList.ToList());
        }
    }
    catch (Exception ex)
    {
        exceptions.Enqueue(ex);
    }
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });
foreach(var info in infos)
{
    workerBlock.Post(info);
}
workerBlock.Complete();