Use of thread local storage tls

2016/09/28 22:00
Reading number 1.4K

Thread Local Storage (TLS) is mainly used to store and maintain thread related data in multiple threads. The stored data will be associated with the current thread and does not need locks for maintenance..

Therefore, there is no problem of resource competition between multiple threads. How to implement TLS storage mainly includes the following ways:

  1. Gcc and clang's __thread Modifier
  2. Msvc's __declspec(thread) Modifier
  3. Pthread library pthread_setspecific and pthread_getspecific Interface
  4. Windows TlsSetValue and TlsGetValue

__Use of thread and __declspec (thread)

Among them, __thread and __declspec (thread) are the most convenient. You just need to add this modifier before static or global variables, and then access variables in the thread

For example:

 tb_void_t tb_thread_func(tb_cpointer_t priv) { //Define a thread local variable static __thread int a = 0; //Initialize this variable and set it to the current thread id if (! a) a = tb_thread_self(); }

If multiple threads are running, the value of variable a of each thread in the above code is different, and the value is the ID of each thread

and __declspec(thread) It is also similar in use, just replace it __thread Just do it

Although these two modifiers are convenient to use, they need compiler support. Although most compilers now support them, they are not enough for cross platform development

After all, there are still many low versions of gcc, which may not be supported __thread , especially in the field of embedded development, the compiler support in the cross compilation tool chain is quite different..

In addition, use __thread For tls data maintenance, you need to manually manage the release of related memory. If you do not use it well, it is easy to cause memory leaks..

Pthread interface

Pthread's tls related interface is relatively complete, and supports the registration of free functions. When a thread exits, it automatically releases the relevant tls data to avoid memory leaks, but it is slightly more complex to use

Let's take a simple example:

 //The key stored in the tls variable in the test thread needs to be defined as global or static static pthread_key_t g_local_key = 0; static tb_void_t tb_thread_local_free(tb_pointer_t priv)   {   tb_trace_i("thread[%lx]: free: %p", tb_thread_self(), priv); }   static tb_void_t tb_thread_local_init(tb_void_t) { //Create the key of the tls and set the automatic release function pthread_key_create(&g_local_key,  tb_thread_local_free); } static tb_int_t tb_thread_local_test(tb_cpointer_t priv) { //In all threads, it is executed only once, which is used to initialize the key of tls inside the thread static pthread_once_t s_once = PTHREAD_ONCE_INIT; pthread_once(&s_once,  tb_thread_local_init); //Try to read the current tls data tb_size_t local; if (! (local = (tb_size_t)pthread_getspecific(g_local_key))) { //Set the tls data as the current thread id tb_size_t self = tb_thread_self(); if (0 == pthread_setspecific(g_local_key, (tb_pointer_t)self)) local = self; } return 0; }

It seems more complex, but more flexible. If you do not need to create a key inside the thread, you do not need to call pthread_once You can directly transfer the created key into the thread for access.

TlsSetValue interface

This socket (TlsSetValue, TlsGetValue, TlsAlloc, TlsFree), The tls operating interface belongs to windows. Of course, it can't be used across platforms. It is similar to that of pthread, but it can't register the automatic release function, and it doesn't provide similar functions pthread_once The interface of creates a key by itself inside the thread, which is slightly inadequate in function..

 static tb_int_t tb_thread_local_test(tb_cpointer_t priv) { //Create a tls key. Note: This is not thread safe. It is better to create it in the init function provided by pthread_once //This is written here for the time being, just to facilitate the description of api usage, don't copy it.. static DWORD s_key = 0; if (! s_key) s_key = TlsAlloc(); //Try to read the current tls data DWORD local; if (! (local = TlsGetValue(s_key)))  { //Set the tls data as the current thread id tb_size_t self = tb_thread_self(); if (TlsSetValue(s_key, (LPVOID)self)) local = self; } return 0; }

In fact, Windows also provides the FlsAlloc and FlsSetValue series interfaces for coroutine use, and supports the registration of automatic release callback functions. However, there are some requirements for the system version, and older systems like xp can't use them.. I won't describe it here.

Provided by tbox thread_local Interface encapsulation

Recently, the tls interface of tbox has been modified, and the implementation logic has been restructured, which has greatly improved the ease of use, functionality and efficiency of cutting..

Currently, the following functions are supported:

  • Support the registration of automatic release callbacks to ensure that the set tls data is automatically released when the thread exits
  • Support thread safe key creation within threads
  • When tbox exits, it will automatically destroy all created keys. Of course, you can also actively destroy it in advance

It is also very convenient to use, very similar to pthread, but it is automatically called internally pthread_once , you don't need to call it explicitly like pthread, for example:

 static tb_void_t tb_demo_thread_local_free(tb_cpointer_t priv) { tb_trace_i("thread[%lx]: free: %p", tb_thread_self(), priv); } static tb_int_t tb_demo_thread_local_test(tb_cpointer_t priv) { /*Thread safely initialize a tls object, equivalent to a key, and register an automatic free callback * *Note: Although all threads will execute to this tb_thread_local_init *However, the tls object of s_local will only be initialized once. There is an internal pthread_once interface to maintain */ static tb_thread_local_t s_local = TB_THREAD_LOCAL_INIT; if (! tb_thread_local_init(&s_local,  tb_demo_thread_local_free)) return -1; //Try to read the current tls data tb_size_t local; if (! (local = (tb_size_t)tb_thread_local_get(&s_local))) { //Set the tls data as the current thread id tb_size_t self = tb_thread_self(); if (tb_thread_local_set(&s_local, (tb_cpointer_t)self)) local = self; } return 0; }

When the thread exits, it will automatically call the free callback to release the corresponding residual tls data, and tb_exit Destroy all created tls objects after exiting

Of course, you can actively call: tb_thread_local_exit(&s_local) To destroy it..

Compared with pthread, this set of interfaces of tbox reduces an init callback function, and has more automatic release mechanisms than that of windows. It also supports cross platform..

Miscellaneous talk

I saw some libraries before that __thread Mixed with the pthread interface, I felt it was inexplicable, and I personally felt that there was a problem, such as:

 static __thread pthread_key_t g_key;

Originally, the pthread document explicitly stated that the key needs global or static storage __thread In fact, each thread accesses different keys..

summary

If you just want to store some simple int data inside the thread and do not consider complete cross platform support, you are recommended to use it directly __thread perhaps __declspec(thread) J is OK, very convenient and easy to use

If you want to consider cross platform operation, the tls interface of tbox is also a good choice..

Personal homepage: TBOOX open source project

Expand to read the full text
Loading
Click to lead the topic 📣 Post and join the discussion 🔥
Reward
zero comment
zero Collection
zero fabulous
 Back to top
Top