Skip to content


So What Is This “Thread Safe” Thing Anyway?

Introduction

Following on from my previous post about a “Thread Safe” Dictionary, and the subsequent comment from Rajeesh, made me think that perhaps a general post on “thread safety” was in order.

Stop With the Quotes Already!

The astute amongst you may notice I always try and put the phrase “thread safe” in quotes; and there is a good reason for that! “Thread safe” is a pretty horrible term (although I can’t think of a better one) that doesn’t really give enough detail for what exactly we mean when we express that a class is “thread safe”. As far as I’m concerned, my “library” classes are thread safe if:

“All public static and instance methods and properties on the class are safe to be called concurrently from multiple threads.”

What this certainly does *not* mean is:

“You do not have to consider thread safety or concurrency issues in your own code that uses this class.”

So in the instance of our dictionary if you get/set a value and then in subsequent lines of code you *assume that the value you got or set hasn’t changed* then you have a concurrency issue in *your* code and you will *need to address it, likely using the same synchronisation primitives we used internally in our SafeDictionary class.*

Some Examples

As an example we can take similar code to the snippet that Rajeesh posted:

// Check the item exists and process
// if it does
if (mySafeDictionary["Testing"] != null)
{
    // Process element
    ProcessElement(mySafeDictionary["Testing"]);
}

Although each of those calls to the SafeDictionary are themselves threadsafe, we are making an assumption in *our code* that those value won’t change between the “if” statement and our call to ProcessElement. This particular scenario is one reason why we should always try to use TryGetValue, rather than the “if it exists do this with it” approach above. To this end we should probably alter our SafeDictionary to explicitly deny gets:

public class SafeDictionary<TKey, TValue>
{
    private readonly object _Padlock = new object();
    private readonly Dictionary<TKey, TValue> _Dictionary = new Dictionary<TKey, TValue>();

    public TValue this[TKey key]
    {
        set
        {
            lock (_Padlock)
            {
                _Dictionary[key] = value;
            }
        }
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        lock (_Padlock)
        {
            return _Dictionary.TryGetValue(key, out value);
        }
    }
}

If you now try and access a member using the array syntax, rather than through TryGetValue you will get a compile time error like:

The property or indexer ‘Variabler.this[string]’ cannot be used in this context because it lacks the get accessor.

From that simple example you may think that TryGetValue solves all our problems; but unfortunately it still isn’t able to save your from yourself! To reiterate what I said earlier, you need to consider concurrency yourself if you *assume that the value you got or set hasn’t changed*. Assuming we expand our SafeDictionary to include all of the options that the normal Dictionary supports, take the following horribly contrived example:

public List<String> GetKeysToProcess()
{
    object myObject;
    List<String> keysToProcess = new List<String>();

    // Check to see if the type is valid for
    // processing
    foreach (var currentKey in myDictionary.Keys)
    {
        if (myDictionary.TryGetValue(currentKey, out myObject))
        {
            if (myObject.GetType() == typeof(ProcessorClass))
                keysToProcess.Add(currentKey);
        }
    }

    return keysToProcess;
}

Now there’s a chance that a key might have been removed between the start of the foreach and us actually using it; but as we use TryGetValue that doesn’t matter – the value just won’t resolve. The key here (pardon the pun) is that we are building and returning a list that *makes assumptions about the contents of the dictionary*. Even if in our consuming code we stick to TryGetValue to handle items being deleted, there’s nothing in this example preventing another thread changing any of the items to be a different type – effectively rendering our List of “Keys to process” completely invalid.

Conclusion

The examples above are very contrived, and I’d hope nobody would ever write those exact pieces of code; but hopefully it illustrates the point that *just because you are working with a “Thread safe” object doesn’t mean you can ignore thread safety concerns in your own code*.

Share:
  • Twitter
  • DotNetKicks
  • DotNetShoutout
  • Google Bookmarks
  • Digg
  • del.icio.us
  • Live
  • Technorati
  • StumbleUpon
  • email
  • Netvibes
  • Ping.fm
  • Print
  • Reddit

Technorati Tags: , , , ,

Posted in .Net, CSharp.

Tagged with , , , , .