You want to store thread-specific data discovered at runtime on a thread that will be accessible only to code running within that thread.
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); } }
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.