Search code examples
c#concurrencyparallel-processingparallel.foreach

ConcurrentBag issue when bag is null and instantiated in loop


Please give me a hint about the following issue:

ConcurrentBag<string> test = null;
List<string> list= new() { "1", "2", "3", "2", "3", "2", "3", "2", "3", "2",
    "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3" };
Parallel.ForEach(list, x => { (test ??= new()).Add("result"); });

=> test will have a different value each time when debugging (e. g. 19 / 23 entries)

ConcurrentBag<string> test = new();
List<string> list= new() { "1", "2", "3", "2", "3", "2", "3", "2", "3", "2",
    "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3", "2", "3" };
Parallel.ForEach(list, x => { (test ??= new()).Add("result"); });

=> test will always have 23 entries

Why does the Parallel.ForEach not properly add the values when it's instantiated with null?


Solution

  • test ??= new()
    

    Is functionally equivalent to:

    if (test == null)
        test = new();
        
    

    You are using multiple threads, so consider this scenario:

    1. Thread 1 reaches the if and sees that test is null. It therefore is about to go on to the next line, when it is pre-empted by thread 2
    2. Thread 2 reaches the if and sees that test is null. It therefore is goes on to create a new ConcurrentBag and assign it to test.
    3. Thread 1 now continues and creates a new ConcurrentBag and assigns it to test, overwriting the one created by thread 2.

    Now you have two instances of ConcurrentBag in use, but only one is assigned to test.