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
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<T> is not thread-safe for instance methods. I usually start at MSDN documentation to determine if I need to lock a resource or not.
I think you mean "multiple threads" not "multiply".
One means many the other is a mathematical operation.
[)amien
thanks, fixed.
now, why didn't you use the threadtester I wrote? it's just for these types of cases..
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).
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?
Simone,
That can certainly be the reason
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...
Ayende, maybe I'm missing the point, so adding to a list is safe as long as you're on a single processor?
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.
Comment preview