7

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

我曾经有这样的功能:

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

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

我认为在 lambdas 中使用局部变量会“捕获”它们的值,但看起来并没有;它总是取 index 的值,就好像该值将在for循环结束时被捕获一样。index在每次迭代中,该变量在 lambda 中被评估为 400,所以我当然得到了IndexOutOfRangeException400 次(items.Count实际上是MAX)。

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

4

2 回答 2

7

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

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变量被更新,并且所有 lambdas 都在捕获同一个变量(使用 lambdas,变量被捕获,而不是values)。

作为旁注,我建议您:

  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);
于 2013-07-10T13:41:21.413 回答
2

您所有的 lambda 表达式都捕获相同的变量,即您的循环变量。但是,只有在循环完成后才会执行所有 lambda。在那个时间点,循环变量具有最大值,因此您的所有 lambdas 都使用它。

Stephen Cleary 在他的回答中展示了如何解决它。

埃里克·利珀特为此写了一个详细的两部分系列

于 2013-07-10T13:44:17.090 回答