.netmultithreadingasynchronousasync-await

.NET C# async await. whenall does not wait for tasks


I have problem with multi threading in .NET Framework 4.5 with await/async/whenall.

I called thousands of threads by calling in loop.

List<Task<string>> t = new List<Task<string>>(); 
for (Row = 0; Row < lines.Count; Row++)
{        
    t.Add(AccessPaymentVault(Row, credentials, cts.Token));
}
string[] str_list = await Task.WhenAll(t.ToArray());

AccessPaymentVault Function is used for connecting vault web service and retrieve credit card information.

async Task<string> AccessPaymentVault(int row, Credentials credentials, CancellationToken ct){
    var data = await Task.Run(() =>
    {
      return Tokenization.Retrieve(credentials, lines[row][CCColumnToken]);
    }, ct);

    return Encryptor.Decrypt(data);
}

Tokenization.Retrieve is the most waited function and it connects to webservice. WhenAll seems to be not waiting all tasks because some records in the result are randomly missing. When I first run, it retrieves all records. In second run, some records in the middle are missing. In third run, the missing records in second run was there but other records are missing.

I guess the problem is in WhenAll but not sure about it.

Any form of help is very appreciated.

Thank you.


Solution

  • Nowadays the suggested way of starting a new task is Task.Run instead of Task.Factory.StartNew, unless you need a higher level of the control over the Task.

    In your case, the main problem is that the result type will be Task, cause some nesting here. But luckily there is a way - Task.Unwrap.

    So if you call the Task.Run - underneath Task.Factory.StartNew and the Task.Unwrap are called automatically.

    Check out this link: Task.Run vs Task.Factory.StartNew

    And another good article: StartNew is Dangerous

    Update from comments

    In terms of using async/await everything looks fine. Just a couple of ideas:

    1. there is a shared state in Tokenization.Retrieve which might be broken because of multithreading
    2. you might swallow some exceptions inside the Tokenization.Retrieve