0

I find an interesting thing that when I tried to add an item into a dictionary in the Parallel loop. It throws a NullReferenceException exception. I can fixed it, but I don't know why it throw that exception. I was wondering if someone can help me clear on that. Below is the source code, it's very simple.

class Program
{
    private static Dictionary<int, Projection> CachedProjections =
        new Dictionary<int, Projection>();

    static void Main(string[] args)
    {
        var key = 3857;
        Parallel.For(0, 400, _ => 
        {
            Projection projection = null; // If remove the null initialization it works well.
            if (CachedProjections.ContainsKey(key))
            {
                projection = CachedProjections[key];
            }
            else
            {
                projection = new Projection();
                CachedProjections[key] = projection;
            }
        });

        Console.Read();
    }
}

public class Projection
{
    public Projection()
    {
        Thread.Sleep(10); // If comment out this line, it works well too.
    }
}
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
yangtam
  • 73
  • 1
  • 8

3 Answers3

1

Dictionary is not thread safe against writes + (anything else). That means you'd need to lock around all the dictionary operations, which probably obviates all the things that you're trying to achieve. You could also look at concurrent-dictionary, but don't assume that it will be faster - it is... complicated and depends on whether you have competing read+read, read+write, write+write, etc.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • A [`ConcurrentDictionary`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=netframework-4.7.2) is faster than using a lock around all operations and is pretty easy to use. – Tim Schmelter Dec 04 '18 at 16:00
  • 3
    @Rango that is a hugely simplistic statement; it *really* depends on what the competing operations are – Marc Gravell Dec 04 '18 at 16:01
  • I just referred to your simplistic statement _"but don't assume that it will be faster - it is... complicated "_ A `ConcurrentDictionary` uses separate locks for each hash bucket so should outperform a normal dictionary which needs a total-lock for all operations. At least if you use it like OP (`ContainsKey` + indexer) – Tim Schmelter Dec 04 '18 at 16:04
0

Because Dictionary is not thread-safe:

A Dictionary can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with write accesses, the collection must be locked during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

For thread-safe alternatives, see the ConcurrentDictionary.

Backs
  • 24,430
  • 5
  • 58
  • 85
0

Dictionary<K, V> is not thread safe. Use Thread-Safe Collections for multithreading applications. In your case use the ConcurrentDictionary<TKey,TValue> Class.

See also: How to: Add and Remove Items from a ConcurrentDictionary

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188