提问者:小点点

Paralell.foreach中的嵌套异步方法


我有一个在其中运行多个异步方法的方法。 我必须遍历一个设备列表,并将该设备传递给这个方法。 我注意到完成这个过程需要很长时间,所以我考虑使用并行foreach,这样它就可以同时对多个设备运行这个过程。

假设这是我的方法。

public async Task ProcessDevice(Device device) {
 var dev = await _deviceService.LookupDeviceIndbAsNoTracking(device);

 var result = await DoSomething(dev);
 await DoSomething2(dev);
}

那么DoSomething2也调用一个异步方法。

public async Task DoSomething2(Device dev) {
 foreach(var obj in dev.Objects) {
  await DoSomething3(obj);
 }
}

设备列表随着时间的推移不断变大,因此这个列表增长得越多,程序完成对每个设备运行ProcessDevice()所花费的时间就越长。 我希望一次处理多个设备。 所以我一直在研究使用parallel.foreach。

Parallel.ForEach(devices, async device => {
 try {
  await ProcessDevice(device);
 } catch (Exception ex) {
  throw ex;
 }
})

在设备完全处理之前,程序似乎正在完成。 我还尝试过创建一个任务列表,然后foreach device,将一个运行ProcessDevice的新任务添加到该列表中,然后等待task.WhenAll(listOfTasks);

var listOfTasks = new List<Task>();
foreach(var device in devices) {
 var task = Task.Run(async () => await ProcessDevice(device));
listOfTasks.Add(task);
}
await Task.WhenAll(listOfTasks);

但似乎在ProcessDevice()实际完成运行之前,任务被标记为已完成。

请原谅我对这个问题的无知,因为我是新的并行处理和不确定是怎么回事。 是什么原因导致了这种行为,您是否可以提供任何文档来帮助我更好地理解该怎么做?


共3个答案

匿名用户

不能将asyncparallel.foreach混合使用。 由于您的底层操作是异步的,所以您希望使用异步并发,而不是并行。 异步并发最容易用WhenAll:

var listOfTasks = devices.Select(ProcessDevice).ToList();
await Task.WhenAll(listOfTasks);

匿名用户

在上一个示例中,有几个问题:

var listOfTasks = new List<Task>();
foreach (var device in devices)
{
    await  Task.Run(async () => await ProcessDevice(device));
}
await Task.WhenAll(listOfTasks);

执行await Task.run(async()=>await ProcessDevice(device));意味着在完成前一个迭代之前,您不会转到foreach循环的下一个迭代。 基本上,你还是在一次一个地做。

此外,您没有向listOfTasks添加任何任务,因此它保持为空,因此Task.WhenAll(listOfTasks)立即完成,因为没有任务等待。

请尝试以下操作:

var listOfTasks = new List<Task>();
foreach (var device in devices)
{
    var task = Task.Run(async () => await ProcessDevice(device))
    listOfTasks.Add(task);
}
await Task.WhenAll(listOfTasks);

匿名用户

我不是很确定这是不是你所要求的,但是我可以给出我们如何启动异步进程的例子

 private readonly Func<Worker> _worker;

    private void StartWorkers(IEnumerable<Props> props){
    Parallel.ForEach(props, timestamp => { _worker.Invoke().Consume(timestamp); });
    }

建议阅读Parallel.foreach,因为它会为您提供一些帮助。