异步lambda中的参数

Max*_*Max 6 c# lambda asynchronous

我试图同时运行几个任务,我遇到了一个似乎无法理解或解决的问题.

我以前有这样的功能:

private void async DoThings(int index, bool b) {
    await SomeAsynchronousTasks();
    var item = items[index];
    item.DoSomeProcessing();
    if(b)
        AVolatileList[index] = item; //volatile or not, it does not work
    else
        AnotherVolatileList[index] = item;
}
Run Code Online (Sandbox Code Playgroud)

我想用for循环调用Task.Run().但是我找不到向这个发送参数的方法,Action<int, bool>并且每个人都建议在类似的情况下使用lambdas:

for(int index = 0; index < MAX; index++) { //let's say that MAX equals 400 
    bool b = CheckSomething();
    Task.Run(async () => {
        await SomeAsynchronousTasks();
        var item = items[index]; //here, index is always evaluated at 400
        item.DoSomeProcessing();
        if(b)
            AVolatileList[index] = item; //volatile or not, it does not work
        else
            AnotherVolatileList[index] = item;
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为在lambdas中使用局部变量会"捕获"它们的值,但它看起来却没有; 它将始终采用索引的值,就好像在for循环结束时捕获该值一样.该index变量在400中在每个迭代拉姆达评价所以当然我得到一个IndexOutOfRangeException400倍(items.Count实际上是MAX).

我真的不确定这里发生了什么(虽然我真的很好奇)但我不知道如何做我想要实现的目标.任何提示都是受欢迎的!

Ste*_*ary 6

制作索引变量的本地副本:

for(int index = 0; index < MAX; index++) {
  var localIndex = index;
  Task.Run(async () => {
    await SomeAsynchronousTasks();
    var item = items[index];
    item.DoSomeProcessing();
    if(b)
        AVolatileList[index] = item;
    else
        AnotherVolatileList[index] = item;
  }
}
Run Code Online (Sandbox Code Playgroud)

这是由于C#for循环的方式:只有一个index变量更新,所有lambdas都捕获相同的变量(使用lambdas,捕获变量,而不是).

作为旁注,我建议你:

  1. 避免async void.您永远不知道async void方法何时完成,并且它们具有困难的错误处理语义.
  2. await所有的异步操作.即,不要忽略从中返回的任务Task.Run.使用Task.WhenAll等等await.这允许异常传播.

例如,这是一种使用方法WhenAll:

var tasks = Enumerable.Range(0, MAX).Select(index =>
  Task.Run(async () => {
    await SomeAsynchronousTasks();
    var item = items[localIndex];
    item.DoSomeProcessing();
    if(b)
        AVolatileList[localIndex] = item;
    else
        AnotherVolatileList[localIndex] = item;
  }));
await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)