异步lambda中的参数

本文关键字:参数 lambda 异步 | 更新日期: 2023-09-27 18:21:45

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

我以前有一个这样的功能:

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;
}

我想使用Task.Run()调用for循环。然而,我找不到向这个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;
    }
}

我原以为在lambdas中使用局部变量会"捕获"它们的值,但看起来并没有;它将总是取index的值,就好像该值将在CCD_。在每次迭代中,index变量在lambda中的值为400,所以我当然得到了400次IndexOutOfRangeExceptionitems.Count实际上是MAX)。

我真的不确定这里发生了什么(尽管我真的很好奇),我也不知道如何实现我想要实现的目标。欢迎任何提示!

异步lambda中的参数

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

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;
  }
}

这是由于C#执行for循环的方式:只有一个index变量被更新,并且所有的Lambda都在捕获同一个变量(使用Lambda,捕获变量,而不是)。

顺便说一句,我建议你:

  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);

所有lambdas都捕获相同的变量,该变量是循环变量。但是,只有在循环完成之后,才会执行所有的lambda。在这个时间点上,循环变量具有最大值,因此所有Lambda都使用它

Stephen Cleary在他的回答中展示了如何修复它。

埃里克·利珀特写了一个由两部分组成的详细系列。