DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 15.12 Storing Thread-Specific Data Privately

Problem

You want to store thread-specific data discovered at runtime on a thread that will be accessible only to code running within that thread.

Solution

Use the AllocateDataSlot or AllocateNamedDataSlot method on the Thread class to reserve a thread local storage (TLS) slot. Using TLS, a large structure can be stored in a data slot on a thread and used in many different methods. This can be done without having to pass the structure as a parameter.

For this example, a structure called Data here represents a structure that can grow to be very large in size:

public struct Data
{
    // Application data is stored here
}

Before using this structure, a data slot has to be created in TLS to store the structure. The following code creates an instance of the Data structure and stores it in the data slot named AppDataSlot:

Data appData = new Data( );
Thread.SetData(Thread.GetNamedDataSlot("appDataSlot"), appData);

Whenever this structure is needed, it can be retrieved with a call to Thread.GetData. The following line of code gets the appData structure from the data slot named appDataSlot:

Data storedAppData = (Data)Thread.GetData(Thread.GetNamedDataSlot("appDataSlot"));

At this point, the storedAppData structure can be read or modified. After the action has been performed on the storedAppData structure, storedAppData must be placed back into the data slot named appDataSlot:

Thread.SetData(Thread.GetNamedDataSlot("appDataSlot"), appData);

Once the application is finished using this structure, the data slot can be released from memory using the following method call:

Thread.FreeNamedDataSlot("appDataSlot");

The following simple class shows how TLS can be used to store a structure:

using System;
using System.Threading;

public class HandleStructure
{
    public static void Main( )
    {
        // Create structure instance and store it in the named data slot
        Data appData = new Data( );
        Thread.SetData(Thread.GetNamedDataSlot("appDataSlot"), appData);

        // Call another method that will use this structure
        HandleStructure.MethodB( );

        // When done, free this data slot
        Thread.FreeNamedDataSlot("appDataSlot");
    }

    public static void MethodB( )
    {
        // Get the structure instance from the named data slot
        Data storedAppData = (Data)Thread.GetData(
          Thread.GetNamedDataSlot("appDataSlot"));

        // Modify the StoredAppData structure

        // When finished modifying this structure, store the changes back 
        // into the named data slot
        Thread.SetData(Thread.GetNamedDataSlot("appDataSlot"), 
                         storedAppData);

        // Call another method that will use this structure
        HandleStructure.MethodC( );
    }

    public static void MethodC( )
    {
        // Get the structure instance from the named data slot
        Data storedAppData = 
             (Data)Thread.GetData(Thread.GetNamedDataSlot("appDataSlot"));

        // Modify the storedAppData structure

        // When finished modifying this structure, store the changes back into
        //    the named data slot
        Thread.SetData(Thread.GetNamedDataSlot("appDataSlot"), storedAppData);
    }
}

Discussion

Thread local storage is a convenient way to store data that is usable across method calls without having to pass the structure to the method or even without knowledge about where the structure was actually created.

Data stored in a named TLS data slot is available only to that thread; no other thread can access a named data slot of another thread. The data stored in this data slot is accessible from anywhere within the thread. This setup essentially makes this data global to the thread.

To create a named data slot, use the static Thread.GetNamedDataSlot method. This method accepts a single parameter, name, that defines the name of the data slot. This name should be unique; if a data slot with the same name exists, then the contents of that data slot will be returned and a new data slot will not be created. This action occurs silently; there is no exception thrown or error code available to inform you that you are using a data slot someone else created. To be sure that you are using a unique data slot, use the Thread.AllocateNamedDataSlot method. This method throws a System.ArgumentException if a data slot already exists with the same name. Otherwise, it operates similarly to the GetNamedDataSlot method.

It is interesting to note that this named data slot is created on every thread in the process, not just the thread that called this method. This fact should not be much more than an inconvenience to you, though, since the data in each data slot can be accessed only by the thread that contains it. In addition, if a data slot with the same name was created on a separate thread and you call GetNamedDataSlot on the current thread with this name, none of the data in any data slot on any thread will be destroyed.

GetNamedDataSlot returns a LocalDataStoreSlot object that is used to access the data slot. Note that this class is not creatable through the use of the new keyword. It must be created through one of the AllocateDataSlot or AllocateNamedDataSlot methods on the Thread class.

To store data in this data slot, use the static Thread.SetData method. This method takes the object passed in to the data parameter and stores it in the data slot defined by the dataSlot parameter.

The static Thread.GetData method retrieves the object stored in a data slot. This method accepts a LocalDataStoreSlot object that is created through the Thread.GetNamedDataSlot method. The GetData method then returns the object that was stored in that particular data slot. Note that the object returned might have to be cast to its original type before it can be used.

The static method Thread.FreeNamedDataSlot will free the memory associated with a named data slot. This method accepts the name of the data slot as a string and, in turn, frees the memory associated with that data slot. Remember that when a data slot is created with GetNamedDataSlot, a named data slot is also created on all of the other threads running in that process. This is not really a problem when creating data slots with the GetNamedDataSlot method because if a data slot exists with this name, a LocalDataStoreSlot object that refers to that data slot is returned, a new data slot is not created, and the original data in that data slot is not destroyed.

This situation becomes more of a problem when using the FreeNamedDataSlot method. This method will free the memory associated with the data slot name passed in to it for all threads, not just the thread that it was called on. Freeing a data slot before all threads have finished using the data within that data slot can be disastrous to your application.

A way to work around this problem is to not call the FreeNamedDataSlot method at all. When a thread terminates, all of its data slots in TLS are freed automatically. The side effect of not calling FreeNamedDataSlot is that the slot is taken up until the garbage collector determines that the thread the slot was created on finished and the slot can be freed.

If you know the number of TLS slots you need for your code at compile time, consider using the ThreadStaticAttribute on a static field of your class to set up TLS-like storage.

See Also

See the "Thread Local Storage and Thread Relative Static Fields," "ThreadStatic-Attribute Attribute," and "Thread Class" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section