异步lambda中的参数

时间:2013-07-10 13:37:52

标签: c# asynchronous lambda

我正在尝试同时执行多项任务,并且遇到了一个我似乎无法理解或解决的问题。

我曾经有过这样的功能:

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

我想使用forTask.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;
    }
}

我认为在lambdas中使用局部变量会“捕获”它们的值但看起来却没有;它将始终采用索引的值,就好像在for循环结束时捕获该值一样。 index变量在每次迭代时在lambda中以400计算,所以当然我得到IndexOutOfRangeException 400次(items.Count实际上是MAX)。

我真的不确定这里发生了什么(尽管我对此非常好奇)并且我不知道如何做我想要实现的目标。欢迎任何提示!

2 个答案:

答案 0 :(得分: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;
  }
}

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

作为旁注,我建议您:

  1. 避免使用async void。您永远不会知道async void方法何时完成,并且它们具有困难的错误处理语义。
  2. await所有异步操作。即,不要忽略从Task.Run返回的任务。对Task.WhenAll await使用WhenAll等。这允许例外传播。
  3. 例如,这是使用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); 的一种方式:

    {{1}}

答案 1 :(得分:1)

所有lambdas都捕获相同的变量,这是你的循环变量。但是,只有在循环结束后才会执行所有lambda。在那个时间点,循环变量具有最大值,因此你的所有lambda都使用它。

斯蒂芬·克利里在答案中表明如何解决这个问题。

Eric Lippert写了一篇关于此的detailled two-part series