代码之家  ›  专栏  ›  技术社区  ›  Dan Nguyen

控制台应用程序中的task.run()打印奇怪的结果[重复]

  •  0
  • Dan Nguyen  · 技术社区  · 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的值当作在 对于 循环。这个 index 变量在每次迭代中的lambda值是400,所以我当然得到了 IndexOutOfRangeException 400次( items.Count 实际上是 MAX )中。

    我真的不知道这里发生了什么(尽管我对此很好奇),我也不知道如何去做我想做的事情。欢迎任何提示!

    0 回复  |  直到 12 年前
        1
  •  6
  •   Stephen Cleary    12 年前

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

    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, 变量 被俘虏了,不是 价值观 )中。

    另外,我建议你:

    1. 避免 async void 是的。你永远不知道什么时候 异步无效 方法完成,并且它们具有困难的错误处理语义。
    2. await 所有异步操作。也就是说,不要忽略从 Task.Run 是的。使用 Task.WhenAll 或者类似于 等待 为了他们。这允许传播异常。

    例如,这里有一种使用 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);
    
        2
  •  1
  •   Daniel Hilgarth Richard    12 年前

    你所有的羔羊 相同的 变量,这是循环变量。但是,所有lambda只在循环完成后执行。在这一点上,循环变量具有最大值,因此所有的变量都使用它。

    斯蒂芬·克利里在他的回答中展示了如何修复它。

    埃里克·利珀特写了一篇 detailled two-part series 关于这个。