[oR]
6.6 Using Thread Local Storage The 32-bit APIs contains a unique feature called Thread Local Storage (TLS), which you can use in processes and DLLs in a variety of ways. The capability itself is very straightforward, but its extended ramifications are what make it interesting.
The TLS capability consists of four very simple functions: TlsAlloc, TlsFree, TlsSetValue, and TlsGetValue. You use TLS by first calling the TlsAlloc function, as shown below:
DWORD tlsIndex; // probably a global variable
...
// allocate a thread local storage index tlsIndex=TlsAlloc();
The call to TlsAlloc creates a block of memory that contains one 32-bit storage location for each existing thread in a process. Any new threads created after the call to TlsAlloc also receive a unique 32-bit storage location. TlsAlloc returns an index value that the process uses when referring to this particular set of 32-bit thread storage locations. The process can call TlsAlloc as many times as it likes, as long as it uses a different variable each time. See Figure 6.4.
Figure 6.4. The relationship between TLS and the threads in a process. TIsIndex references a block of storage that contains one 32-bit value for each thread in the process
Inside any thread, a call to the function TlsGetValue with the TlsIndex value returns the 32-bit value from TLS storage associated with that particular thread. That is, the value returned by TlsGetValue is context-sensitive based on whatever thread is calling the function. The same goes for TlsSetValue.
The TlsFree function frees up the space occupied by all of the storage locations.
The TLS capability is frequently used inside of DLLs to store pointers to blocks of memory associated with the different threads calling the DLL. See Chapter 17 for details.
Listing 6.11 demonstrates TLS in a simple program. You should compare this program with Listing 6.5, because they do approximately the same thing in different ways.
Listing 6.11 Using Thread Local Storage (TLS). Compare with Listing 6.5
// thread4.cpp
#include <windows.h> #include <stdlib.h> #include <iostream.h>
DWORD tlsIndex;
typedef struct { DWORD frequency; DWORD duration; } honkParams;
void ParamsAlloc()
{ honkParams *params; params=(honkParams *) GlobalAlloc(GPTR, sizeof(honkParams)); // generate a randomish value for the frequency params->frequency=GetTickCount() & 0x00000FFF; cout << "Using frequency: " << params->frequency << endl; params->duration=100; // save the pointer value relevant // for the calling thread TlsSetValue(tlsIndex, params); }
void ParamsFree() { honkParams *params; // retrieve the pointer value relevant // for the calling thread params=(honkParams *) TlsGetValue(tlsIndex); GlobalFree(params); }
void Honk(void) { honkParams *params; // retrieve the pointer value relevant // for the calling thread params=(honkParams *) TlsGetValue(tlsIndex); Beep(params->frequency, params->duration); }
void HonkThread() { DWORD i; // use a general purpose "params" // allocation function ParamsAlloc(); for (i=0; i<8; i++) { Honk(); Sleep(1000); } // use a general purpose "params" free function ParamsFree(); }
void main(void) { HANDLE honkHandles[3]; DWORD threadID; DWORD count; // allocate a thread local storage index tlsIndex=TlsAlloc(); for (count=0; count<3; count++) { // create a thread which beeps honkHandles[count]=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) HonkThread, 0, 0, &threadID); Sleep(1500); } WaitForMultipleObjects(3, honkHandles, TRUE, INFINITE); // free the thread local storage index TlsFree(tlsIndex); }
The code in Listing 6.11 declares a global variable named TlsIndex. The program's first action in the main function is to allocate a TLS storage block pointed to by TlsIndex. The main function then creates three threads and waits for their completion before freeing the TLS storage space.
TlsAlloc |
Allocates a TLS storage structure |
DWORD TlsAlloc(VOID)
|
Returns an index to the TLS storage structure, or 0xFFFFFFFF on failure |
TlsFree |
Frees a TLS storage structure |
BOOL TlsFree( DWORD index)
|
Index |
The index of an existing TLS structure |
Returns TRUE on success |
� |
Each thread calls the ParamsAlloc function, which allocates a parameter structure on the heap and fills it with values for the thread. The pointer to the parameter block is placed in the TLS area using TlsSetValue. TlsSetValue chooses the TLS location in which to place the pointer based on the thread that calls the function. To later free up its parameter storage block, each thread calls ParamsFree. This function retrieves the pointer from the TLS and frees the block.
TlsGetValue |
Retrieves a value from a TLS structure based on the thread making the call |
LPVOID TlsGetValue( DWORD index)
|
index |
The index of an existing TLS structure |
Returns the value for the thread on success, or 0 on failure |
TlsSetValue |
Sets a value into a TLS structure based on the thread making the call |
BOOL TlsSetValue( DWORD index, LPVOID value)
|
index |
The index of an existing TLS structure |
value |
The value to store |
Returns TRUE on success |
The TLS capability could be used to implement the parameter storage for the multi-threaded Mandelbrot program shown in Listing 6.10, especially since each thread is in charge of cleaning up its own allocated memory in that program. The TLS capability is also frequently used in DLLs to keep memory allocations in the DLL separated on a thread-by-thread basis. See Chapter 17 for details.
|
No comments:
Post a Comment