emailGitHubLinkedIn

Beware Thread.FreeNamedDataSlot (and other tips regarding thread-local storage in .NET)

Recently I had to resolve an issue with a .NET data-access library storing data in thread-specific named data slots. The library used named data slots (via System.Threading.Thread's GetData and SetData methods) to store static variables without sharing them between threads. But when using this library in a WCF service called by two programs at the same time, a problem occurred: after one program finished using the library, the second program failed.

The culprit? Thread.FreeNamedDataSlot When a call to the library finished using the data in thread-local storage, it called this method to dispose of the data. But a careful reading of its documentation reveals that it doesn't just free the data slot of the current thread—it frees the data slot with that name in every thread in the process. So in our WCF service, hosted as a single process in IIS, when the first data-access library call finished, the call to Thread.FreeNamedDataSlot freed the thread-local memory still in use by the second program, causing the error.

One shouldn't scold the author of the library too harshly. Named data slots are specific to a thread, so it's reasonable to expect Thread.FreeNamedDataSlot to act only on the current thread. But it doesn't, so note its true purpose when using thread-local storage.

Incidentally, you may prefer to store thread-specific data in static variables decorated with ThreadStaticAttribute instead. These offer type safety, and according to the documentation, they perform faster than named data slots.

But neither named data slots nor thread-static variables are completely safe to use in ASP.NET applications (such as our WCF service mentioned above). This article article explains why; in short, ASP.NET may swap the thread servicing a request, and data local to the thread won't carry over. Data in HttpContext.Current.Items, however, will carry over, so when developing a web application or web service, consider storing your thread-static data there instead. Or, if you're writing a library and don't know what sort of application will use it, you can write a conditional check like the following, as mentioned in the article:

if (HttpContext.Current != null)
{
    HttpContext.Current.Items[VARIABLE_NAME] = value;
}
else
{
    threadStaticVariable = value;
}

where threadStaticVariable is a static variable decorated with ThreadStaticAttribute.

Keeping these tips in mind should help you avoid writing code with bugs difficult to locate later.