Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loop count != list.Count - Populating List<string> in new threads

Why is the result (list.Count) of the following code always something around 18100 and not 19000 as expected?

    var list = new List<string>(19000);
    List<Task> tl = new List<Task>(19000);

    for (int q = 0; q < 19000; q++)
    {
        tl.Add(Task.Factory.StartNew(() =>
        {
            var k = "something";
            list.Add(k);
        }));
    }

    Task.WaitAll(tl.ToArray());
    Console.WriteLine(list.Count);
like image 611
Legends Avatar asked May 10 '26 08:05

Legends


2 Answers

The problem is that the List<T> is not thread safe.

Task Parallelism

You could work with thread-safe collections in the System.Collections.Concurrent and use the type ConcurrentBag<>. Try using this:

var list = new ConcurrentBag<string>();

List<Task> tl = new List<Task>(19000);

for (int q = 0; q < 19000; q++)
{
    tl.Add(Task.Factory.StartNew(() =>
    {
        var k = "something";
        list.Add(k);
    }));
}

Task.WaitAll(tl.ToArray());
Console.WriteLine(list.Count);

There are other thread-safe types like ConcurrentQueue<>, ConcurrentStack<>, etc.

Dealing with Non Thread-Safe objects - lock(object)

On the other hand, you could use the lock keyworkd to block other threads to access the same block of statements. In this case, you could use the List<T> and non-thread-safe objects, for sample:

The object must be visible for all threads, so, you could declare it on a class scope, for sample:

private static object _sync = new object();

After it, try lock this instance when you need to modify, for sample:

var list = new List<string>(19000);

List<Task> tl = new List<Task>(19000);

for (int q = 0; q < 19000; q++)
{
    tl.Add(Task.Factory.StartNew(() =>
    {
       lock(_sync)
       {
          var k = "something";
          list.Add(k);
       }
    }));
}

Task.WaitAll(tl.ToArray());
Console.WriteLine(list.Count);
like image 191
Felipe Oriani Avatar answered May 12 '26 21:05

Felipe Oriani


The List<T> Class isn't thread safe see MSDN

Thread Safety

Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

It is safe to perform multiple read operations on a List, but issues can occur if the collection is modified while it’s being read. To ensure thread safety, lock the collection during a read or write operation. To enable a collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization. For collections with built-in synchronization, see the classes in the System.Collections.Concurrent namespace. For an inherently thread–safe alternative, see the ImmutableList class.

like image 40
Martin Avatar answered May 12 '26 21:05

Martin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!