Ayende @ Rahien

Unnatural acts on source code

Adding to list from multiple threads?

I just read an email from someone that assume that it is safe to access a collection from multiple threads, if this is done for adding items to the collection only.

My gut feeling said that this is a bad idea, but I set out to verify it anyway. Here is the test case: 

static void Main(string[] args)
{
    List<int> ints = new List<int>();
    ManualResetEvent resetEvent = new ManualResetEvent(false);
    int additions = 0;
    int totalCount = 1000000;
    for (int i = 0; i < totalCount; i++)
    {
        int tmp = i;//check C# spec for reasons
        ThreadPool.QueueUserWorkItem(delegate
         {
             resetEvent.WaitOne();
             ints.Add(tmp);
Interlocked.Increment(ref additions); }); } Console.WriteLine("Starting to add..."); resetEvent.Set(); while (additions < totalCount) Thread.Sleep(200); Console.WriteLine(additions); Console.WriteLine(ints.Count); }

It is a bit tricky, because all code that deals with multi threading is tricky, but basically I am trying to create as much contention as possible, and on my machine (dual core), I get the following results:

Starting to add...
1000000
999812

So the answer is pretty clear, it is not safe to access a collection from multiply threads without proper locks, not matter what you do to it. I had to increate the totalCount significantly before I could get a reproducible result, but this happened at much lower iteration counts as well, so don't think that you can get away with it if you have small collections.

Working with threads requires thread synchronization, always.

Comments

Jimmy Bogard
07/05/2007 10:30 PM by
Jimmy Bogard

MSDN documentation will tell you if a class is thread-safe or not:

http://msdn2.microsoft.com/en-us/library/6sh2ey19.aspx

Down at the bottom in the "Thread Safety" section, it says that List is not thread-safe for instance methods. I usually start at MSDN documentation to determine if I need to lock a resource or not.

Damien Guard
07/06/2007 06:39 AM by
Damien Guard

I think you mean "multiple threads" not "multiply".

One means many the other is a mathematical operation.

[)amien

roy osherove
07/06/2007 04:06 PM by
roy osherove

now, why didn't you use the threadtester I wrote? it's just for these types of cases..

James Kovacs
07/06/2007 04:55 PM by
James Kovacs

Just throwing in my 2 cents supporting Oren's example above. I always wonder why developers insist on "clever" algorithms, especially involving multi-threading. They use gut-feel to tell them whether a lock is required or whether finer-grained locking is appropriate. If you think multi-threading is easy, you haven't written enough multi-threaded code. Implement the simplest locking scheme possible (with unit tests to prove it actually works). Then when you measure a perf bottleneck in the locking code, implement a more performant algorithm and ensure your tests still pass. Premature optimization is the root of all evil. Premature optimization of locking code is evil incarnate (and just plain stupid).

SimoneB
07/10/2007 04:49 PM by
SimoneB

Ayende, I'm having no troubles running your code, it runs fine with no errors in the additions... Can it be because your machine is a dual core?

Ayende Rahien
07/10/2007 06:19 PM by
Ayende Rahien

Simone,

That can certainly be the reason

Simone Busoli
07/11/2007 12:29 PM by
Simone Busoli

However it looks strange to me... I should be experimenting at least some issues since so many threads are trying to add items to the list concurrently, but instead I have no troubles doing it, as if the list was thread safe...

Simone Busoli
07/12/2007 09:22 PM by
Simone Busoli

Ayende, maybe I'm missing the point, so adding to a list is safe as long as you're on a single processor?

Ayende Rahien
07/12/2007 10:23 PM by
Ayende Rahien

No, it is never safe without proper locking in place.

On a single processor, you have simply not been able to reliably reproduce the issue, but it certainly exists.

Comments have been closed on this topic.