Friday, August 31, 2007

Thread-safety for generic collections in C# 2.0

Recently I had to fix the thread safety issue with a generic dictionary in one of the Spindrift system components. The problem is that no generic collections in C# provides thread-safe methods or at least wrapper classes. My problem was in particular with generic Dictionary class.

MSDN summarizes the threading safety for generic Dictionary in the following paragraph:

''A System.Collections.Generic.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. To guarantee thread safety during enumeration, you can lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.''

This is a major issue for correct functioning of the multi-threaded system such as Spindrift. This is fixed in version 3.0 of the MS .NET implementation (see System.Collection.Generic Namespace entry on MSDN]) but to be compliant with Mono we decided to keep things up to C# 2.0 standard.

There are three things you can do about it (as outlined by titus here)
  • subclass thread usafe class - cons: tedious, also need to reimplement enumerator (see MSDN Dictionary documentation entry as above)
  • create utility/helper class doing sychronisation for you - cons: all clients for that generic class have to use that helper class to prevent unclean reads
  • creating your own implementation of particular collection class

There is actually an another and easy fix. This assumes, however, that you never use enumerators in different threads for the same collection object. You can simply wrap any section of code modifing the collection in your client code using lock.

I initially fixed the problem by adding the critical section around code sections modifying the collection, but of course this means that you need to rememeber to do that for every collection modification you might introduce later. Then I searched through the classes using generic Dictionary in the whole system and this is what I got:

  1. DocumentFrequency.cs
  2. LinkJustificationFormatter.cs
  3. TermFrequency.cs
  4. Job.cs
  5. Node.cs
  6. SimpleXMLJobParser.cs
  7. XmlRpcConnector.cs
  8. ProcessingState.cs
  9. DocumentCache.cs
  10. Porter2Test.cs
  11. TermFrequencyTest.cs

As much as I would not be concerned much about the test classes system components such as TermFrequency or DocumentCache or Job or Node classes can result in significant issues when multiple threads will be executed in parallel.

I quickly wrote a wrapper for generic Dictionary class but it took much too long to write wrappers for all collections used in the system. The realistic solution is probably to write utility/helper classes for the generic collection classes.