TLSAlloc ()

Source: Internet
Author: User

Why should I have TLS? The reason for this is that the global variables in the process and static variables defined within the function are shared variables that each thread can access. The memory content that is modified in one thread is in effect for all threads. This is an advantage as well as a disadvantage. Say it is the advantage, the data exchange of the thread becomes very fast. Say it is a disadvantage, a thread died, other threads are also life-insured; Multiple threads access shared data, require expensive synchronization overhead, and are prone to synchronization-related bugs.

A new mechanism is required if you need a variable that can be accessed by each function call within a thread, but not accessible by other threads (known as static memory local to a thread threads local static variable). This is TLS.

Thread-local storage has different implementations on different platforms, and portability is not very good. Fortunately, to implement thread-local storage is not difficult, the simplest way is to create a global table, through the current thread ID to query the corresponding data, because each thread's ID is different, the data found naturally different.

Most platforms provide a method of thread-local storage, and we do not need to implement them ourselves:

  Linux:

int Pthread_key_create (pthread_key_t *key, Void (*destructor) (void*));

int Pthread_key_delete (pthread_key_t key);

void *pthread_getspecific (pthread_key_t key);

int pthread_setspecific (pthread_key_t key, const void *value);

  Win32

Method One: Each thread is created when the system assigns it an array of lpvoid pointers (called the TLS Array), which is hidden from the C programming angle and is not directly accessible and needs to be accessed through some C API function calls. First define some DWORD thread global variables or function static variables, ready to access your own TLS array as an index variable for each thread. When a thread uses TLS, the first step calls the TlsAlloc () function within the thread, associating a TLS array index variable with a slot (slot) for the TLS array of the threads, such as getting an index variable:

Global_dwtlsindex=tlsalloc ();

Note that after this step, the current thread actually accesses the copy version within the thread of this TLS array index variable. It is also said that while different threads appear to be using the same name as the TLS array index variable, the actual threads may actually get different DWORD values. The implication is that each TLS-using thread obtains a thread-local static variable of type DWORD as an index variable of the TLS array. C + + has no mechanism to directly define thread-local static variables , so it's a lot of trouble.

The second step is to dynamically allocate a chunk of memory for the current thread (using the LocalAlloc () function call) and then place a pointer to the memory area into the corresponding slot in the TLS array (using the Tlsvalue () function call).

In the third step, within any function of the current thread, you can use the TlsGetValue () function to get a pointer to the memory area of the previous step using the index variable of the TLS array, and then you can read and write the memory area. This implements variables that are accessible everywhere within a thread.

Finally, if you no longer need a local static variable for the above thread, you can dynamically release the memory area (using the LocalFree () function) and discard the corresponding slot (using the TlsFree () function) from the TLS array.

The

tls  is a good win32  trait, making multithreaded programming easier. tls  is a mechanism through which programs can have global variables but are in a state of "each thread is different". That is, all threads in a process can have global variables, but these variables are specific to a thread. For example, you might have a multithreaded program in which each thread writes files to different files (and therefore they use different file handle). In this case, it will be very convenient to store the files used by each thread in the tls  handle . When the thread needs to know the handle used, it can be obtained from tls . The point is: the code that the thread uses to get the file handle  is the same in any case, and the file handle  from TLS is different. Very dexterous, isn't it? It is convenient to have global variables, but it belongs to each thread.   
 

  Although tls  is convenient, it is not unlimited. Among Windows nt  and Windows 95 , there are 64  DWORD slots  for each thread to use. This means that a process can have a maximum of 64  "different meanings for each thread" DWORDs.   Although tls  can store a single value such as file handle, the more common use is to place pointers, pointing to the private data of the thread. In many cases, multi-threaded routines need to store a bunch of data, and they are all related to each thread. Many programmers do this by wrapping these variables as c  structures and then storing the structure pointers in the tls . When a new thread is born, the program configures some memory for the structure to use, and stores the pointer in a tls  reserved for the thread. Once the thread ends, the program code frees all configured chunks. Since each thread has 64  slots  to store the thread's own data, where does this space come from? In the thread learning we can see from the structure TDB, each thread database  has 64  dwords  for tls  use . When you set or take out data in a tls  function, you actually face the DWORDs. Well, now we know that those "global variables that have different meanings to each thread" are the tdb of the respective threads in the online process.  
 

Next you may ask: how do I access these 64 DWORDs? How do I know which DWORDs is occupied, which is not occupied? First of all, we must understand the fact that the system provides us with TLS as a convenient way to implement the "global variables that have different meanings to each thread", and since we want to achieve the effect of "global variables", that is to say, each thread needs to use this variable. In that case, we don't need to mark the 64 DWORDs of each thread, because one of the 64 DWORDs is occupied and the DWORD of all the threads is consumed, so KERNEL32 uses two DWORDs (64 total BITS) to record which slot is available and which slot is already in use. These two dwords can be imagined as a 64-bit array, and if a bit is established, it means that its corresponding TLS slot is already in use. This 64-bit TLS slot array is stored in process database (we have listed those two DWORDs in the PDB structure in the process section).

The following four functions operate on TLS:

(1) TlsAlloc

We said it above. KERNEL32 uses two DWORDs (total 64 bits) to record which slot is available and which slot is already in use. When you need to use a TLS slot, you can use this function to put the corresponding TLS slot location 1.

(2) TlsSetValue

The tlssetvalue can put data into the TLS slot that was previously configured. The two parameters are the TLS slot index value and the data content to be written. TlsSetValue The data you specify into an array of DWORDs (located in the current thread database) in the appropriate location.

(3) TlsGetValue

This function is almost tlssetvalue Mirror, the biggest difference is that it takes out data rather than setting data. Like tlssetvalue , this function checks whether the TLS index value is legal or not. If so,TlsGetValue uses this index value to find the corresponding data item in the DWORDs array (in the thread database) and passes its contents back.

(4) TlsFree

This function erases all efforts of TlsAlloc and tlssetvalue . TlsFree First Check if the index value you gave it is indeed configured. If it is, it turns off the corresponding 64-bit TLS slots bit. Then, to avoid the use of the content that is no longer valid,TlsFree each thread in the process and puts 0 on the TLS slot that was just released. So, if a TLS index is later reconfigured, all threads that use the index will be guaranteed to fetch a value of 0 unless they call TlsSetValueagain.

Mutex (mutex) is a very versatile kernel object. The ability to guarantee mutually exclusive access to the same shared resource by multiple threads. Similar to the critical section, only the line friend with the mutex has permission to access the resource, because there is only one mutex object, so it is determined that the shared resource will not be accessed by multiple threads at the same time in any case. The thread that currently occupies the resource should hand over the owning mutex after the task has been processed so that other threads can access the resource after it is acquired. Unlike several other kernel objects, mutexes have special code in the operating system and are managed by the operating system, and the operating system even allows it to perform unconventional operations that other kernel objects cannot. For ease of understanding, refer to the working model of the mutex kernel object given in Figure 3.8:

Figure 3.8 Protection of shared resources using mutually exclusive kernel objects

The arrow in figure (a) is the thread that accesses the resource (rectangular box), but only the second thread has a mutex (black point) and is allowed to enter the shared resource, while the other threads are excluded ((b)). When this thread finishes processing the shared resource and prepares to leave the zone, it will hand over the mutex it owns ((c)), and any other thread attempting to access the resource has the opportunity to obtain the mutex.

The main functions that can be used to maintain thread synchronization with mutually exclusive kernel objects are CreateMutex, OpenMutex, ReleaseMutex, WaitForSingleObject, and WaitForMultipleObjects. Before using a mutex object, you first create or open a mutex object through CreateMutex or OpenMutex. The CreateMutex function is prototyped as follows:

HANDLE CreateMutex (

Lpsecurity_attributes lpmutexattributes,//Security attribute pointer

BOOL Binitialowner,//initial owner

LPCTSTR lpname//Mutex object name

);

The parameter binitialowner is primarily used to control the initial state of the mutex object. It is generally set to false to indicate that the mutex was not occupied by any thread when it was created. If you specify an object name when creating a mutex, you can get a handle to the mutex from the OpenMutex function elsewhere in the process or in another process. The OpenMutex function prototype is:

HANDLE OpenMutex (
DWORD dwdesiredaccess,//Access flag
BOOL bInheritHandle,//Inheritance flag
LPCTSTR lpname//Mutex object name
);

When a thread that currently has access to a resource no longer needs to access the resource and is leaving, it must pass the ReleaseMutex function to release its own mutex object, whose function prototype is:

BOOL ReleaseMutex (HANDLE Hmutex);

Its only argument Hmutex is the handle of the mutex to be released. As for the WaitForSingleObject and waitformultipleobjects wait functions, the role played by the mutex in maintaining thread synchronization is basically consistent with that of other kernel objects, and is also a notification waiting for the mutex kernel object. However, it should be noted here that the return value of the wait function is no longer the usual wait_object_0 (for the WaitForSingleObject function) or wait_object_0 to Wait_ when the mutex notifies the calling waiting function to return. A value between object_0+ncount-1 (for the WaitForMultipleObjects function), but instead returns a Wait_abandoned_0 (for the WaitForSingleObject function) or wait_ A value between Abandoned_0 and Wait_abandoned_0+ncount-1 (for the WaitForMultipleObjects function), which indicates that the mutex the thread is waiting for is owned by another thread. This thread is terminated before the shared resource is used. In addition, the method of using the mutex object is different from the other kernel objects, while the other kernel objects are not notified, and the thread will hang and lose its scheduling when it is called waiting for the function of the thread. The use of mutually exclusive methods can still have the ability to schedule while waiting, which is one of the unconventional operations that the mutex can accomplish.
When writing a program, a mutex is used to protect a block of memory that is accessed by multiple threads, ensuring that any thread has a reliable exclusive access to it when it processes the block of memory. The sample code given below is the exclusive access protection of the thread through the mutex kernel object Hmutex to the shared memory fast g_carray[]. Here is the sample code:

Mutex Object

HANDLE Hmutex = NULL;

Char g_carray[10];

UINT ThreadProc1 (LPVOID pparam)

{

Waiting for mutex object notification

WaitForSingleObject (Hmutex, INFINITE);

Write to a shared resource

for (int i = 0; i <; i++)

{

G_carray[i] = ' a ';

Sleep (1);

}

Releasing mutex objects

ReleaseMutex (Hmutex);

return 0;

}

UINT ThreadProc2 (LPVOID pparam)

{

Waiting for mutex object notification

WaitForSingleObject (Hmutex, INFINITE);

Write to a shared resource

for (int i = 0; i <; i++)

{

G_carray[10-i-1] = ' B ';

Sleep (1);

}

Releasing mutex objects

ReleaseMutex (Hmutex);

return 0;

}

The use of threads makes program processing more flexible, and this flexibility can also lead to uncertainties. This is especially true when multiple threads are accessing the same public variable. While program code that does not use thread synchronization may not be a logical problem, it is necessary to take thread synchronization measures in place to ensure that the program is running correctly and reliably.

3.2.6 Thread Local Storage

Thread-local Storage (thread-local storage, TLS) is a system that uses convenient storage of thread-local data. The TLS mechanism enables you to associate several data for all threads in a process, and each thread accesses data associated with itself through a global index assigned by TLS. In this way, each thread can store data locally at rest on a wired path.

The data structure used to manage TLS is simple, Windows maintains only one bit array for each process in the system, and then requests an array space of the same length for each thread in the process, as shown in 3.9.

Figure 3.9 Data structures used internally by the TSL mechanism

Each process running in the system has a bit array as shown in Figure 3.9. The member of a bit array is a flag, and the value of each flag is set to free or inuse, indicating whether the array index corresponding to this flag is in use. WINDODWS is guaranteed to have at least tls_minimum_available (defined in the WinNT.h file) with a flag bit available.

The typical steps for using TLS dynamically are as follows.

(1) The main thread calls the TlsAlloc function to assign an index to the thread-local storage, the function prototype is:

DWORD TlsAlloc (void); Returns a TLS index

As mentioned above, the system maintains a bit array of length tls_minimum_available for each process, and the return value of TlsAlloc is a subscript (index) of the array. The only use of this bit array is to memorize which subscript is in use. In the initial state, the value of this bit array member is free, indicating that it is not being used. When the TlsAlloc is called, the system checks the values of the members in the array, until a member with a value of free is found. After the value of the found member is changed from free to inuse, the TlsAlloc function returns the index of the member. If a member with a value of free cannot be found, the TlsAlloc function returns tls_out_of_indexes (defined in the WinBase.h file as-1), which means failure.

For example, at the first call to TlsAlloc, the system discovers that the value of the first member in the bit array is free, it changes the value of this member to InUse, and then returns 0.

When a thread is created, Windows assigns the thread an array of length tls_minimum_available in the process address space, and the values of the array members are initialized to 0. Internally, the system associates this array with the thread, guaranteeing that the data in this array can only be accessed in that thread. As shown in 3.7, each thread has its own array, and the array members can store any data.

(2) Each thread calls TlsSetValue and TlsGetValue to set or read the values in the thread array, and the function prototype is:

BOOL TlsSetValue (

DWORD Dwtlsindex,//TLS Index

LPVOID Lptlsvalue//value to set

);

LPVOID TlsGetValue (DWORD dwtlsindex); TLS index

The TlsSetValue function puts the value specified by the parameter lptlsvalue into the member of the thread array that is indexed to Dwtlsindex. In this way, the value of Lptlsvalue is associated with the thread that called the TlsSetValue function. This function call succeeds and returns True.

Calling the TlsSetValue function, a thread can only change the value of a member in its own thread array, and there is no way to set the TLS value for another thread. Until now, the only way to path data from one thread to another is to use the parameters of the thread function when creating the thread.

The role of the TlsGetValue function is to get the value of the member that is indexed to Dwtlsindex in the thread array.

TlsSetValue and TlsGetValue are used to set and get the values of specific members in the thread array, and the index they use is the return value of the TlsAlloc function. This fully illustrates the relationship between the unique bit array in the process and the array of threads. For example, TLSALLOC returns 3, which means that index 3 is saved by each running and later thread in the process to access the values of the corresponding members in the respective thread array.

(3) The main thread calls TlsFree to release the local storage index. The only argument to the function is the index returned by the TlsAlloc.

With TLS, you can associate a data to a particular thread. For example, the following example associates the creation time of each thread with that thread, so that the thread's life cycle can be obtained when the threads are terminated. The code for the entire trace thread run time example is as follows:

#include <stdio.h>//03UseTLS Project

#include <windows.h>

#include <process.h>

Using TLS to track the running time of a thread

DWORD G_tlsusedtime;

void Initstarttime ();

DWORD Getusedtime ();

UINT __stdcall ThreadFunc (LPVOID)

{int i;

Initialization start time

Initstarttime ();

Simulate long-time work

i = 10000*10000;

while (i--) {}

Print out the time that this thread ran

printf ("This thread was coming to end. Thread ID:%-5d, used time:%d \ n ",

:: GetCurrentThreadID (), Getusedtime ());

return 0;

}

int main (int argc, char* argv[])

{UINT uId;

int i;

HANDLE H[10];

Initializes the thread run time recording system by requesting an index in the process bit array

G_tlsusedtime =:: TlsAlloc ();

Make 10 threads run simultaneously and wait for their respective output results

for (i=0; i<10; i++)

{H[i] = (HANDLE):: _beginthreadex (NULL, 0, threadfunc, NULL, 0, &UID); }

for (i=0; i<10; i++)

{:: WaitForSingleObject (H[i], INFINITE);

:: CloseHandle (H[i]); }

Frees the resources used by the time-logging system by releasing the thread-local storage index

:: TlsFree (G_tlsusedtime);

return 0;

}

Start time of the initialization thread

void Initstarttime ()

{//Gets the current time, and the thread's creation time is associated with the thread object

DWORD Dwstart =:: GetTickCount ();

:: TlsSetValue (G_tlsusedtime, (LPVOID) dwstart);

}

Gets the time that a thread has run

DWORD Getusedtime ()

{//Gets the current time, returns the difference between the current time and the thread creation time

DWORD dwelapsed =:: GetTickCount ();

dwelapsed = dwelapsed-(DWORD):: TlsGetValue (G_tlsusedtime);

return dwelapsed;

}

The GetTickCount function can take the time that Windows has elapsed since startup, and its return value is the time in milliseconds that started.

In general, the task of assigning TLS indexes to each thread is done in the main thread, and the assigned index values should be kept in global variables to facilitate access by each thread. The above example code is clear to illustrate this point. The main thread begins by using TlsAlloc to request an index for the time tracking system, which is saved in the global variable G_tlsusedtime. After that, 10 threads were created at the same time for the characteristics of the example TLS mechanism. These 10 threads finally print out their life cycle, as shown in 3.10.

3.10 Life cycle of each thread

This simple thread-running time recording system only provides Initstarttime and getusedtime two functions for the user to use. The Initstarttime function should be called at the beginning of the thread, and after this function gets the current time, call TlsSetValue to save the threads ' creation time in an array of g_tlsusedtime-indexed threads. When you want to see the run time of a thread, call the Getusedtime function directly. This function uses TlsGetValue to get the creation time of the thread, and then returns the difference between the current time and the creation time.

Http://www.cnblogs.com/lzjsky/archive/2010/09/01/1814843.html

TLSAlloc ()

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.