async/await和Parallel.For循环

Pab*_*blo 1 c# asynchronous winforms task-parallel-library

我以两种不同的方式运行Parallel.For循环:

  1. 不写任何async/await但只是调用Task ReadSensorAsync(int):

    var watch = System.Diagnostics.Stopwatch.StartNew();
    Parallel.For(3, 38,
    index => {
        ReadSensorsAsync(index);
    });
    watch.Stop();
    Console.WriteLine("Parallel finished in {0} ms", watch.ElapsedMilliseconds);
    
    Run Code Online (Sandbox Code Playgroud)

    时间显示 1ms

  2. 跑步 async/await

    var watch = System.Diagnostics.Stopwatch.StartNew();
    Parallel.For(3, 38,
    async index => {
        await ReadSensorsAsync(index);
    });
    watch.Stop();
    Console.WriteLine("Parallel finished in {0} ms", watch.ElapsedMilliseconds);
    
    Run Code Online (Sandbox Code Playgroud)

    这表明2-5ms.

问题是为什么存在这种差异以及使用哪种方式.

svi*_*ick 5

首先,这两个版本都没有任何意义.Parallel.For用于运行CPU绑定(或可能阻止IO绑定)操作.您正在使用它来启动异步操作.

你不是在等待操作完成,你说这是故意的,但它也很危险:如果发生异常ReadSensorsAsync,你就无法捕捉它.

由于启动异步操作应该非常快,要一次启动其中许多,你不需要Parallel.For,你可以使用normal for:

for (int i = 3; i < 38; i++)
{
    ReadSensorsAsync(i);
}
Run Code Online (Sandbox Code Playgroud)

(但同样,我不建议忽略返回Task.)


至于计时,最大的区别可能是因为你忽略了热身:当你ReadSensorsAsync第一次打电话时,它必须是JIT编译的,对于这种简单的操作,它会显着地扭曲结果.

这是我机器上的数字,格式是"第一次运行"; "第二次跑":

  • 呼叫ReadSensorsAsync一次(用于比较):7.6 ms; 0.04毫秒
  • for:7.5毫秒; 0.05毫秒
  • Parallel.For没有await:8.0毫秒; 0.5毫秒
  • Parallel.Forawait:11毫秒; 2.6毫秒

如您所见,使用Parallel.For仅增加了开销.使用它await会增加更多的开销,因为启动一个async方法需要创建一个状态机,这需要一些时间.