Search code examples
c#asynchronousconcurrencytask

Task.Run(), passing parameter to


Consider the following code:

attempt = 0;
for (int counter = 0; counter < 8; counter++)
{
    if (attempt < totalitems)
    {
        Tasklist<output>.Add(Task.Run(() =>
        {
            return someasynctask(inputList[attempt]);
        }));
    }
    else
    {
        break;
    }
    attempt++;
}
await Task.WhenAll(Tasklist).ConfigureAwait(false);

I want to have for example 8 concurrent tasks, each working on different inputs concurrently, and finally check the result, when all of them have finished. Because I'm not awaiting for completion of Task.Run() attempt is increased before starting of tasks, and when the task is started, there may be items in the inputList that are not processed or processed twice or more instead (because of uncertainty in attempt value.

How to do that?


Solution

  • Thanks to @gobes for his answer:

    Try this:

    attempt = 0;
    for (int counter = 0; counter < 8; counter++)
    {
        if (attempt < totalitems)
        {
            Tasklist<output>.Add(Task.Run(() =>
            {
                int tmpAttempt = attempt;
                return someasynctask(inputList[tmpAttempt]);
            }));
        }
        else
        {
            break;
        }
        attempt++;
    }
    await Task.WhenAll(Tasklist).ConfigureAwait(false);
    

    Actually, what the compiler is doing is extracting your lambda into a method, located in an automagically generated class, which is referencing the attempt variable. This is the important point: the generated code only reference the variable from another class; it doesn't copy it. So every change to attempt is seen by the method.

    What happens during the execution is roughly this:

    enter the loop with attempt = 0 add a call of the lambda-like-method to your tasklist increase attempt repeat After the loop, you have some method calls awaiting (no pun intended) to be executed, but each one is referencing THE SAME VARIABLE, therefore sharing its value - the last affected to it.

    For more details, I really recommend reading C# in depth, or some book of the same kind - there are a lot of resources about closure in C# on the web :)