异步Lambda中的参数 [英] Parameters in asynchronous lambdas

查看:72
本文介绍了异步Lambda中的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to run several tasks at the same time and I came across an issue I can't seem to be able to understand nor solve.

我曾经有过这样的功能:

I used to have a function like this :

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>的方法,每个人都建议在类似情况下使用lambda:

That I wanted to call in a for loop using Task.Run(). However I could not find a way to send parameters to this Action<int, bool> and everyone recommends using lambdas in similar cases:

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,因此我当然得到了IndexOutOfRangeException 400倍(items.Count实际上是MAX).

I thought using local variables in lambdas would "capture" their values but it looks like it does not; it will always take the value of index as if the value would be captured at the end of the for loop. The index variable is evaluated at 400 in the lambda at each iteration so of course I get an IndexOutOfRangeException 400 times (items.Count is actually MAX).

我真的不确定这里发生了什么(尽管我对此很好奇),我也不知道该怎么做才能达到的目标.欢迎任何提示!

I am really not sure about what is happening here (though I am really curious about it) and I don't know how to do what I am trying to achieve either. Any hints are welcome!

推荐答案

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

Make a local copy of your index variable:

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,变量是捕获的,而不是).

This is due to the way C# does a for loop: there is only one index variable that is updated, and all your lambdas are capturing that same variable (with lambdas, variables are captured, not values).

作为旁注,我建议您:

  1. 避免使用async void.您永远无法知道async void方法何时完成,并且它们具有难以处理的错误语义.
  2. await您的所有异步操作.即,不要忽略Task.Run返回的任务.将Task.WhenAll等用于await.这样可以传播异常.
  1. Avoid async void. You can never know when an async void method completes, and they have difficult error handling semantics.
  2. await all of your asynchronous operations. I.e., don't ignore the task returned from Task.Run. Use Task.WhenAll or the like to await for them. This allows exceptions to propagate.

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

For example, here's one way to use 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);

这篇关于异步Lambda中的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆