Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is AddOrUpdate thread safe in ConcurrentDictionary?

I tried to use AddOrUpdate method in ConcurrentDictionary.

From the "Remarks" section on this page https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx. it says

"However, delegates for these methods are called outside the locks to avoid the problems that can arise from executing unknown code under a lock. Therefore, the code executed by these delegates is not subject to the atomicity of the operation."

So I am not sure whether it is thread safe. I have one case, if the key is not found, the value is 1, otherwise increase the value by 1.

I wrote below function

    private static void AddOrUpdate(ConcurrentDictionary<string, int> map)
    {
        Random r = new Random();
        Thread.Sleep(r.Next(10));
        map.AddOrUpdate(Key, 1, (key, value) => value + 1);
    }

    public static void TestThreadSafe(ConcurrentDictionary<string, int> map)
    {
        Thread[] threads = new Thread[Size];
        for (int i = 0; i < Size; ++i)
        {
            threads[i] = new Thread(() => AddOrUpdate(map));
        }

        foreach (var thread in threads)
        {
            thread.Start();
        }
    }

created about 300,000 threads and run them in parallel. The results are always 300,000.

Is above method thread safe? When is AddOrUpdate not thread safe?

like image 733
nop Avatar asked Aug 26 '16 00:08

nop


1 Answers

It is thread safe in your usage. It becomes not thread safe when the delegate passed to AddOrUpdate has side effects, because those side effects may be executed twice for the same key and existing value.

For example:

private static void AddOrUpdate(ConcurrentDictionary<string, int> map)
    {
        Random r = new Random();
        Thread.Sleep(r.Next(10));
        map.AddOrUpdate(Key, 1, (key, value) => { Console.WriteLine(key + ": " + value); return value + 1; });
    }

May print the same key + value more than once.

What happens is that sometimes the ConcurrentDictionary may execute those methods on multiple threads, then take the resulting value and enter the lock to try and apply it. One of the threads will succeed, and the other will then enter the lock, see that the value has changed since it read it, and try the delegate again.

like image 139
Chris Shain Avatar answered Oct 21 '22 05:10

Chris Shain