Race condition with .ContinueWith ()

I am working in race conditions, quite difficult to reproduce, and from my analysis of the code, this seems to come from a continuation that is not executed (or not completely).

Here's some pseudocode to represent the context:

Task<Something> GetObject(string id)
{
    // some async code retrieving the object
}

List<Task> tasks = new List<Task>();
List<Something> result = new List<Something>();
foreach (var id in someList)
{
    tasks.Add(GetObject(id).ContinueWith(task => 
    {
        var something = task.Result;
        // do some basic sync stuff on something
        result.Add(something);
    }));
}

await Task.WhenAll(tasks);

      

It happens by chance that some of the expected objects are missing as a result.

For some reason, I know I foreach

went well and there is no reason why all the required tasks would not have been added to List<Task>

.

Tooling or debugging to no avail as it improves the race condition ... I hope someone can shed some lights here. Thank!

+3


source to share


2 answers


For some reason I know foreach went well and no why all the required tasks were not added to the List.

Well, maybe this list is not thread safe.



I don't know from your problem if the list "someList" is short or long, or may have duplicate values, otherwise consider an add lock and / or an Immutable list .

The following link has a good solution: List <T> thread safety

+3


source


The list is not thread safe. Therefore, if 2 threads (or more!) Want to add an item to it, anything can happen (in the documentation). In reality, you will probably have some added elements that are missing.

If you want to continue using the list, you will have to synchronize yourself (perhaps using locks or more complex mechanisms).

You can also use a different type of collection that suits your needs. The types from System.Collections.Concurrent namespace are guaranteed to be thread safe, but of course a little more expensive than the usual old list.



Finally, you feel like your example doesn't need any sort of collection manipulation: Task.WhenAll () returns Task<TResult[]>

. You can rewrite your code this way:

Something[] result;
result = await Task.WhenAll(somelist.Select(id => GetObject(id));

      

It's much shorter, more efficient, and hopefully easier to read.

+2


source







All Articles