如何让Parallel等待所有异步任务完成?

时间:2019-01-29 11:06:45

标签: c#

我正在制作资源加载代码。但是,有一个问题。

var toDoList = new List<Action>();

toDoList.Add(async () =>
{
  await CanvasBitmap.LoadAsync(PATH1);
  Console.WriteLine("Load image");
});
toDoList.Add(async () =>
{
  await CanvasBitmap.LoadAsync(PATH2);
  Console.WriteLine("Load image");
});

// ...

toDoList.Add(async () =>
{
  await CanvasBitmap.LoadAsync(PATH100);
  Console.WriteLine("Load image");
});

toDoList.Add(() =>
{
  AudioSystem.Instance.Load(AUDIO_PATH1);
  Console.WriteLine("Load audio");
});

toDoList.Add(() =>
{
  AudioSystem.Instance.Load(AUDIO_PATH2);
  Console.WriteLine("Load audio");
});

// ...

toDoList.Add(() =>
{
  AudioSystem.Instance.Load(AUDIO_PATH100);
  Console.WriteLine("Load audio");
});

Console.WriteLine("Parallel Load Start");
Parallel.ForEach(toDoList, toDo => {
  toDo();
});
Console.WriteLine("Parallel Load End");

我想让Parallel等待所有LoadAsync函数完成。但是,由于异步Action委托,我没有等待。它表明了

并行加载开始
加载音频
加载音频
载入图片
加载音频
载入图片
...
加载音频
平行负载端
载入图片
载入图片
载入图片
...

由于它是Win2D的一部分,因此没有同步加载图像的方法。 (= WinRT API)
我了解为什么所有磁盘I / O任务都必须是异步方法。
但是,以上代码不是在UI线程上运行,而是在自定义线程上运行。因此,它不需要运行异步。
您可以认为,由于异步方法,并行是无用的。但是,正如您所看到的,toDoValues具有异步(Win2D)和同步(音频)。需要并行才能有效加载音频文件。

如何让Parallel等待所有资源加载完毕?

2 个答案:

答案 0 :(得分:3)

  

需要并行才能有效加载音频文件。

嗯,Parallel是执行并行操作的一种方法。但是您的代码当前正在混合使用CPU约束的操作和异步操作,并尝试使用Parallel将它们组合在一起,Task.Run仅用于 。这就是为什么它不能很好地工作的原因。

更好的方法是使用Parallel而不是Task.WhenAll将CPU绑定的操作推送到线程池中。然后,您可以将它们作为异步对象进行处理(从UI线程的角度来看),然后使用var toDoList = new List<Func<Task>>(); toDoList.Add(async () => { await CanvasBitmap.LoadAsync(PATH1); Console.WriteLine("Load image"); }); toDoList.Add(async () => { await CanvasBitmap.LoadAsync(PATH2); Console.WriteLine("Load image"); }); // ... toDoList.Add(async () => { await CanvasBitmap.LoadAsync(PATH100); Console.WriteLine("Load image"); }); toDoList.Add(() => Task.Run(() => { AudioSystem.Instance.Load(AUDIO_PATH1); Console.WriteLine("Load audio"); })); toDoList.Add(() => Task.Run(() => { AudioSystem.Instance.Load(AUDIO_PATH2); Console.WriteLine("Load audio"); })); // ... toDoList.Add(() => Task.Run(() => { AudioSystem.Instance.Load(AUDIO_PATH100); Console.WriteLine("Load audio"); })); Console.WriteLine("Concurrent Load Start"); var tasks = toDoList.Select(toDo => toDo()).ToList(); Console.WriteLine("Concurrent Load Started"); await Task.WhenAll(tasks); Console.WriteLine("Concurrent Load End"); 将它们与自然异步的I / O操作结合起来。

使用示例代码,如下所示:

Rmisc

答案 1 :(得分:1)

您需要使用WhenAll

Console.WriteLine("Parallel Load Start");
Parallel.ForEach(toDoList, toDo => {
  toDo();
});

await Task.WhenAll(toDoList); // Next line will be executed when all tasks are completed,

Console.WriteLine("Parallel Load End");