Книга: Programming with POSIX® Threads

5.4.1 Creating thread-specific data

5.4.1 Creating thread-specific data

pthread_key_t key;

int pthread_key_create (

pthread_key_t *key,

void (*destructor)(void *));

int pthread_key_delete (pthread_key_t key);

A thread-specific data key is represented in your program by a variable of type pthread_key_t. Like most Pthreads types, pthread_key_t is opaque and you should never make any assumptions about the structure or content. The easiest way to create a thread-specific data key is to call pthread_key_create before any threads try to use the key, for example early in the program's main function.

If you need to create a thread-specific data key later, you have to ensure that pthread_key_create is called only once for each pthread_key_t variable. That's because if you create a key twice, you are really creating two different keys. The second key will overwrite the first, which will be lost forever along with the values any threads might have set for the first key.

When you can't add code to main, the easiest way to ensure that a thread-specific data key is created only once is to use pthread_once, the one-time initialization function, as shown in the following program, tsd_once.c.

7-10 The tsd_t structure is used to contain per-thread data. Each thread allocates a private tsd_t structure, and stores a pointer to that structure as its value for the thread-specific data key tsd_key. The thread_id member holds the thread's identifier (pthread_t), and the string member holds the pointer to a "name" string for the thread. The variable tsd_key holds the thread-specific data key used to access the tsd_t structures.

19-27 One-time initialization (pthread_once) is used to ensure that the key tsd_key is created before the first access.

33-56 The threads begin in the thread start function thread_routine. The argument (arg) is a pointer to a character string naming the thread. Each thread calls pthread_once to ensure that the thread-specific data key has been created. The thread then allocates a tsd_t structure, initializes the thread_id member with the thread's identifier, and copies its argument to the string member.

The thread gets the current thread-specific data value by calling pthread_ getspecific, and prints a message using the thread's name. It then sleeps for a few seconds and prints another message to demonstrate that the thread-specific data value remains the same, even though another thread has assigned a different tsd_t structure address to the same thread-specific data key.

? tsd_once.c

1 #include <pthread.h>
2 #include "errors.h"
3
4 /*
5 * Structure used as the value for thread-specific data key.
6 */
7 typedef struct tsd_tag {
8  pthread_t thread_id;
9  char *string;

10 } tsd_t;
11
12 pthread_key_t tsd_key; /* Thread-specific data key */
13 pthread_once_t key_once = PTHREAD_ONCE_INIT; 14
15 /*
16 * One-time initialization routine used with the pthread_once
17 * control block.
18 */
19 void once_routine (void)
20 {
21  int status;
22
23  printf ("initializing keyn");
24  status = pthread_key_create (&tsd_key, NULL);
25  if (status != 0)
26  err_abort (status, "Create key");
27 }
28
29 /*
30 * Thread start routine that uses pthread_once to dynamically
31 * create a thread-specific data key.
32 */
33 void *thread_routine (void *arg)
34 {
35  tsd_t *value;
36  int status;
37
38  status = pthread_once (&key_once, once_routine);
39  if (status != 0)
40  err_abort (status, "Once init");
41  value = (tsd_t*)malloc (sizeof (tsd_t));
42  if (value == NULL)
43  errno_abort ("Allocate key value");
44  status = pthread_setspecific (tsd_key, value);
45  if (status != 0)
46  err_abort (status, "Set tsd");
47  printf ("%s set tsd value %pn", arg, value);
48  value->thread_id = pthread_self ();
49  value->string = (char*)arg;
50  value = (tsd_t*)pthread_getspecific (tsd_key);
51  printf ("%s starting...n", value->string);
52  sleep (2);
53  value = (tsd_t*)pthread_getspecific (tsd_key);
54  printf ("%s done...n", value->string);
55  return NULL;
56 }

57
58 void main (int argc, char *argv[])
59 {
60  pthread_t threadl, thread2;
61  int status;

62
63  status = pthread_create (
64  &thread1, NULL, thread_routine, "thread 1");
65  if (status != 0)
66  err_abort (status, "Create thread 1");
67  status = pthread_create (
68  &thread2, NULL, thread_routine, "thread 2");
69  if (status != 0)
70  err_abort (status, "Create thread 2");
71  pthread_exit (NULL);
72 }

Pthreads allows you to destroy a thread-specific data key when your program no longer needs it, by calling pthread_key_delete. The Pthreads standard guarantees only 128 thread-specific data keys at any one time, so it may be useful to destroy a key that you know you aren't using and won't need again. The actual number of keys supported by your Pthreads system is specified by the value of the symbol PTHREAD_KEYS_MAX defined in <limits.h>.

When you destroy a thread-specific data key, it does not affect the current value of that key in any thread, not even in the calling thread. That means your code is completely responsible for freeing any memory that you have associated with the thread-specific data key, in all threads. Of course, any use of the deleted thread-specific data key (pthread_key_t) results in undefined behavior.

Delete thread-specific data keys only when you are sure no thread has a value for that key!

Or,.. don't destroy them at all.

You should never destroy a key while some thread still has a value for that key. Some later call to pthread_key_create, for example, might reuse the pthread_key_t identifier that had been assigned to a deleted key. When an existing thread that had set a value for the old key requests the value of the new key, it will receive the old value. The program will likely react badly to receiving this incorrect data, so you should never delete a thread-specific data key until you are sure that no existing threads have a value for that key, for example, by maintaining a "reference count" for the key, as shown in the program tsd_destructor.c. in Section 5.4.3.

Even better, don't destroy thread-specific data keys. There's rarely any need to do so, and if you try you will almost certainly run into difficulties. Few programs will require even the minimum Pthreads limit of 128 thread-specific data keys. Rarely will you use more than a few. In general, each component that uses thread-specific data will have a small number of keys each maintaining pointers to data structures that contain related data. It would take a lot of components to exhaust the available keys!

Оглавление книги


Генерация: 1.094. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз