Older version of StringEnum throws error when accessed by multiple threads

C#, English No Comments »

A C# enumeration refers to a set of named constants. Every value inside an enumerator list has a default integer type which starts at zero and, if not specified otherwise, gets automatically increased by one:

enum WebsiteType {
Blog = 0,
News = 1,
Youtube = 2  };

Which is completely equivalent to:

enum WebsiteType {
Blog,
News,
Youtube  };

But not to:

enum WebsiteType {
Blog = 2,
News = 4,
Youtube = 6  };

Enumeration values can only be associated with integer values, if you want to associate an enum value with a string you will need to enhance the enumeration’s behavior. To achieve it,  I have been using the “StringEnum” class from the Code associate .NET project for quite a while now.  The StringEnum class is a helper class that allows you to associate string value to enum values such as:

public enum Animal {
[StringValue("Domestic cat")]
CAT = 0,
[StringValue("Domestic dog")]
DOG = 1,
[StringValue("Domestic golden fish")]
FISH = 3
}

Once you need to retrieve the value associated with a specific value of the enumeration, you can do the following:

string enumValue = "";
enumValue = StringEnum.GetStringValue(Animal.CAT))

It offers a very practical behavior that I have been using throughout a lot of different projects. Nevertheless, I recommend you to use the up-to date version of the class since the one I have been using is not thread safe. I downloaded it a while ago and never bothered to check for a newer version.

Recently, I created a small ASP.NET image gallery for a contest around the anniversary of the Mexican revolution and independence. People were given the right to upload an image and to vote for an image. I configured log4net in such a way that every error that got caught was sent to me by email. It is that way I discovered that an error is sometimes raised when different threads are working with the StringEnum class. I found it quite interesting since I have been using log4net and StringEnum on a lot of projects and the error had never be thrown before.

For performance reason, the StringEnum class holds a static hashtable with all the string values from the enumeration. This helps reducing the look-up costs caused by the reading of the enumerator’s attributes. When you request the StringValue of a particular enumeration value, the StringEnum class queries its hashtable. If it founds the value then it returns it otherwise it adds the new requested value.

The problem is that the hashtable is a static attribute which means it is shared by all the instances of the StringEnum class. Since there is no control over the insertion into the hashtable, when several threads were accessing it, a “System.ArgumentException” could be thrown:

error_stringEnum

Although the error message is shown in Spanish, it is quite clear that a thread was trying to add a value to the hashtable that already existed! This is not a big surprise since the MSDN documentation states that a “Hashtable is thread safe for use by multiple reader threads and a single writing thread“.  When working with multiple writing threads, things starts to go wrong.

I uploaded a small 2008 Visual Studio solution with a performance test that I applied to the old, the new version of the StringEnum class, and also to a custom version I wrote. Before writing to the hashtable, every thread must request a lock on it, this prevents multiple writing from happening. We need to make sure that while a thread is writing a new value to the hashtable, no other threads can come in and do the same thing. This is where you need to use a lock.

The new version of  StringEnum from the Code Associate locks directly on the hashtable. Although this approach is working, I prefer to use an additional variable to lock on. Creating “lock variables” makes your code a bit cleaner and prevent from having two pieces of code blocking on the same object. In the words of John Skeet: “I believe this is a bad idea (locking on a instance), because it means you have less control over your locks. Other code may well end up locking on the same object as you do within your code, which makes it far harder to ensure that you only obtain locks in an appropriate order“.

If you try out the solution, you will see that the old version of StringEnum (CA_old directory) fails while the newer version (CA_new directory) and mine (CA_custom directory) pass the test without throwing exception. Finally, if you look through my code you will see I added a line of code inside every StringEnum  class that forces the thread to sleep for 12 milliseconds. I added this line in order to provoke more quickly a writing/reading error from a thread. And for those how prefer not to download the project, the method looks like the following, where _stringValue is our hastable:

GetStringValueWithSleep

Download the 2008 Visual Studio solution with the test case.

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in