線程私有數據(Thread-specific data,TSD):存儲和查詢與某個線程相關數據的一種機制。
在進程內的所有線程都共享相同的地址空間,即意味著任何聲明為靜態或外部變量,或在進程堆聲明的變量,都可以被進程內所有的線程讀寫。
一個線程真正擁有的唯一私有存儲是處理器寄存器,棧在“主人”故意暴露給其他線程時也是共享的。
有時需要提供線程私有數據:可以跨多個函數訪問(全局);僅在某個線程有效(私有)(即在線程裡面是全局)。例如:errno。
進程中的所有線程都可以訪問進程的整個地址空間,除非使用寄存器(一個線程真正擁有的唯一私有存儲是處理器寄存器),線程沒有辦法阻止其它線程訪問它的數據,線程私有數據也不例外,但是管理線程私有數據的函數可以提高線程間的數據獨立性。
進程內的所有線程共享進程的數據空間,因此全局變量為所有線程所共有。但有時線程也需要保存自己的私有數據,這時可以創建線程私有數據(Thread-specific Date)TSD來解決。在線程內部,私有數據可以被各個函數訪問,但對其他線程是屏蔽的。例如我們常見的變量errno,它返回標准的出錯信息。它顯然不能是一個局部變量,幾乎每個函數都應該可以調用它;但它又不能是一個全局變量。(即在線程裡面是全局變量)
創建線程私有數據就是為了線程內部各個函數可以很容易的傳遞數據信息,因此要使線程外的函數不能訪問這些數據,而線程內的函數使用這些數據就像線程內的全局變量一樣,這些數據在一個線程內部是全局的,一般用線程私有數據的地址作為線程內各個函數訪問該數據的入口。
線程私有數據采用了一種被稱為一鍵多值的技術,即一個鍵對應多個數值。訪問數據時都是通過鍵值來訪問,好像是對一個變量進行訪問,其實是在訪問不同的數據。使用線程私有數據時,首先要為每個線程私有數據創建一個相關聯的鍵。在各個線程內部,都使用這個公用的鍵來指代線程數據,但是在不同的線程中,這個鍵代表的數據是不同的。操作線程私有數據的函數主要有4個:pthread_key_create(創建一個鍵),pthread_setspecific(為一個鍵設置線程私有數據),pthread_getspecific(從一個鍵讀取線程私有數據),pthread_key_delete(刪除一個鍵)。
創建一個鍵:
[cpp] view plaincopyprint
01.int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));//返回值:若成功則返回0,否則返回錯誤編號
int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));//返回值:若成功則返回0,否則返回錯誤編號 在分配(malloc)線程私有數據之前,需要創建和線程私有數據相關聯的鍵(key),這個鍵的功能是獲得對線程私有數據的訪問權。
如果創建一個線程私有數據鍵,必須保證pthread_key_create對於每個Pthread_key_t變量僅僅被調用一次,因為如果一個鍵被創建兩次,其實是在創建兩個不同的鍵,第二個鍵將覆蓋第一個鍵,第一個鍵以及任何線程可能為其關聯的線程私有數據值將丟失。
創建新鍵時,每個線程的私有數據地址設為NULL。
注意:創建的鍵存放在keyp指向的內存單元,這個鍵可以被進程中的所有線程使用,但每個線程把這個鍵與不同的線程私有數據地址進行關聯。
除了創建鍵以外,pthread_key_create可以選擇為該鍵關聯析構函數,當線程退出時,如果線程私有數據地址被置為非NULL值,那麼析構函數就會被調用。
注意:析構函數參數為退出線程的私有數據的地址。如果私有數據的地址為NULL,就說明沒有析構函數與鍵關聯即不需要調用該析構函數。
當線程調用pthread_exit或者線程執行返回,正常退出時,析構函數就會被調用,但是如果線程調用了exit、_exit、Exit函數或者abort或者其它非正常退出時,就不會調用析構函數。
線程通常使用malloc為線程私有數據分配空間,析構函數通常釋放已分配的線程私有數據的內存。
線程可以為線程私有數據分配多個鍵,每個鍵都可以有一個析構函數與它關聯。各個鍵的析構函數可以互不相同,當然它們也可以使用相同的析構函數。
線程退出時,線程私有數據的析構函數將按照操作系統實現定義的順序被調用。析構函數可能調用另外一個函數,而該函數可能創建新的線程私有數據而且把這個線程私有數據和當前的鍵關聯起來。當所有的析構函數都調用完成以後,系統會檢查是否有非NULL的線程私有數據值與鍵關聯,如果有的話,再次調用析構函數,這個過程一直重復到線程所有的鍵都為NULL值線程私有數據,或者已經做了PTHREAD_DESTRUCTOR_ITERATIONS中定義的最大次數的嘗試。
取消鍵與線程私有數據之間的關聯:
[cpp] view plaincopyprint
01.int pthread_delete(pthread_key_t *keyp);//返回值:若成功則返回0,否則返回錯誤編號
int pthread_delete(pthread_key_t *keyp);//返回值:若成功則返回0,否則返回錯誤編號 注意調用pthread_delete不會激活與鍵關聯的析構函數。刪除線程私有數據鍵的時候,不會影響任何線程對該鍵設置的線程私有數據值,甚至不影響調用線程當前鍵值,所以容易造成內存洩露(因為鍵不與私有數據關聯了,當線程正常退出的時候不會調用鍵的析構函數,最終導致線程的私有數據這塊內存沒有釋放)。使用已經刪除的私有數據鍵將導致未定義的行為。
注意:對於每個pthread_key_t變量(即鍵)必須僅調用一次pthread_key_create。如果一個鍵創建兩次,其實是在創建不同的鍵,第二個鍵將覆蓋第一個,第一個鍵與任何線程可能為其設置的值將一起永遠的丟失。所以,pthread_key_create放在主函數中執行;或每個線程使用pthread_once來創建鍵。
線程私有數據與鍵關聯:
[cpp] view plaincopyprint
01.int pthread_setspecific(pthread_key_t key,const void *value);//返回值:若成功則返回0,否則返回錯誤編號
02.void* pthread_getspecific(pthread_key_t key);//返回值:線程私有數據地址;若沒有值與鍵關聯則返回NULL
int pthread_setspecific(pthread_key_t key,const void *value);//返回值:若成功則返回0,否則返回錯誤編號
void* pthread_getspecific(pthread_key_t key);//返回值:線程私有數據地址;若沒有值與鍵關聯則返回NULL 如果沒有線程私有數據值與鍵關聯,pthread_getspecific鍵返回NULL,可以依據此來確定是否調用pthread_setspecific。
注意:兩個線程對自己的私有數據操作是互相不影響的。也就是說,雖然 key 是同名且全局,但訪問的內存空間並不是相同的一個。key 就像是一個數據管理員,線程的私有數據只是到他那去注冊,讓它知道你這個數據的存在。
示例代碼:
[cpp] view plaincopyprint
01.#include <stdio.h>
02.#include <pthread.h>
03.#include <stdlib.h>
04.
05.typedef struct private_tag {
06. pthread_t thread_id;
07. char *string;
08.} private_t;
09.
10.pthread_key_t identity_key; /* Thread-specific data key */
11.pthread_mutex_t identity_key_mutex = PTHREAD_MUTEX_INITIALIZER;
12.long identity_key_counter = 0;
13.
14.
15.void identity_key_destructor (void *value)
16.{
17. private_t *private = (private_t*)value;
18. int status;
19.
20. printf ("thread \"%s\" exiting...\n", private->string);
21. free (value);
22. status = pthread_mutex_lock (&identity_key_mutex);
23. if (status != 0)
24. perror("pthread_mutex_lock");
25. identity_key_counter--;
26. if (identity_key_counter <= 0) {
27. status = pthread_key_delete (identity_key);
28. if (status != 0)
29. perror("pthread_key_delete");
30. printf ("key deleted...\n");
31. }
32. status = pthread_mutex_unlock (&identity_key_mutex);
33. if (status != 0)
34. perror("pthread_mutex_unlock");
35.}
36.
37.void *identity_key_get (void)
38.{
39. void *value;
40. int status;
41.
42. value = pthread_getspecific (identity_key);
43. if (value == NULL) {
44. value = malloc (sizeof (private_t));
45. if (value == NULL)
46. perror ("malloc");
47. status = pthread_setspecific (identity_key, (void*)value);
48. if (status != 0)
49. perror("pthread_setspecific");
50. }
51. return value;
52.}
53.
54.void *thread_routine (void *arg)
55.{
56. private_t *value;
57.
58. value = (private_t*)identity_key_get ();
59. value->thread_id = pthread_self ();
60. value->string = (char*)arg;
61. printf ("thread \"%s\" starting...\n", value->string);
62. sleep (2);
63. return NULL;
64.}
65.
66.void main (int argc, char *argv[])
67.{
68. pthread_t thread_1, thread_2;
69. private_t *value;
70. int status;
71.
72. status = pthread_key_create (&identity_key, identity_key_destructor);
73. if (status != 0)
74. perror("pthread_key_create");
75. identity_key_counter = 3;
76. value = (private_t*)identity_key_get ();
77. value->thread_id = pthread_self ();
78. value->string = "Main thread";
79. status = pthread_create (&thread_1, NULL,thread_routine, "Thread 1");
80. if (status != 0)
81. perror("pthread_create");
82. status = pthread_create (&thread_2, NULL,thread_routine, "Thread 2");
83. if (status != 0)
84. perror("pthread_create");
85. pthread_exit (NULL);
86.}
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
typedef struct private_tag {
pthread_t thread_id;
char *string;
} private_t;
pthread_key_t identity_key; /* Thread-specific data key */
pthread_mutex_t identity_key_mutex = PTHREAD_MUTEX_INITIALIZER;
long identity_key_counter = 0;
void identity_key_destructor (void *value)
{
private_t *private = (private_t*)value;
int status;
printf ("thread \"%s\" exiting...\n", private->string);
free (value);
status = pthread_mutex_lock (&identity_key_mutex);
if (status != 0)
perror("pthread_mutex_lock");
identity_key_counter--;
if (identity_key_counter <= 0) {
status = pthread_key_delete (identity_key);
if (status != 0)
perror("pthread_key_delete");
printf ("key deleted...\n");
}
status = pthread_mutex_unlock (&identity_key_mutex);
if (status != 0)
perror("pthread_mutex_unlock");
}
void *identity_key_get (void)
{
void *value;
int status;
value = pthread_getspecific (identity_key);
if (value == NULL) {
value = malloc (sizeof (private_t));
if (value == NULL)
perror ("malloc");
status = pthread_setspecific (identity_key, (void*)value);
if (status != 0)
perror("pthread_setspecific");
}
return value;
}
void *thread_routine (void *arg)
{
private_t *value;
value = (private_t*)identity_key_get ();
value->thread_id = pthread_self ();
value->string = (char*)arg;
printf ("thread \"%s\" starting...\n", value->string);
sleep (2);
return NULL;
}
void main (int argc, char *argv[])
{
pthread_t thread_1, thread_2;
private_t *value;
int status;
status = pthread_key_create (&identity_key, identity_key_destructor);
if (status != 0)
perror("pthread_key_create");
identity_key_counter = 3;
value = (private_t*)identity_key_get ();
value->thread_id = pthread_self ();
value->string = "Main thread";
status = pthread_create (&thread_1, NULL,thread_routine, "Thread 1");
if (status != 0)
perror("pthread_create");
status = pthread_create (&thread_2, NULL,thread_routine, "Thread 2");
if (status != 0)
perror("pthread_create");
pthread_exit (NULL);
查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/
}運行結果:
[cpp] view plaincopyprint
01.huangcheng@ubuntu:~$ ./a.out
02.thread "Main thread" exiting...
03.thread "Thread 2" starting...
04.thread "Thread 1" starting...
05.thread "Thread 2" exiting...
06.thread "Thread 1" exiting...
07.key deleted...
08.huangcheng@ubuntu:~$
huangcheng@ubuntu:~$ ./a.out
thread "Main thread" exiting...
thread "Thread 2" starting...
thread "Thread 1" starting...
thread "Thread 2" exiting...
thread "Thread 1" exiting...
key deleted...
huangcheng@ubuntu:~$
示例代碼2:
[cpp] view plaincopyprint
01.#include <stdio.h>
02.#include <pthread.h>
03.#include <stdlib.h>
04.
05.typedef struct tsd_tag{
06. pthread_t thread_id;
07. char *string;
08.}tsd_t;
09.
10.pthread_key_t key;
11.pthread_once_t once = PTHREAD_ONCE_INIT;
12.
13.void once_routine(void)
14.{
15. int status;
16.
17. printf("Initializing key\n");
18. status = pthread_key_create(&key, NULL);
19. if(status != 0){
20. perror("pthread_key_create");
21. }
22.}
23.
24.void *thread_routine(void *arg)
25.{
26. int status;
27. tsd_t *value = NULL;
28.
29. status = pthread_once(&once, once_routine);
30. if(status != 0){
31. perror("pthread_once");
32. }
33.
34. value = (tsd_t *)malloc(sizeof(tsd_t));
35. if(value == NULL){
36. perror("malloc");
37. }
38.
39. status = pthread_setspecific(key, (void *)value);
40. if(status != 0){
41. perror("pthread_setspecific");
42. }
43.
44. printf("%s set tsd value at %p\n", (char *)arg, value);
45. value->thread_id = pthread_self();
46. value->string = (char *)arg;
47.
48. printf("%s starting......\n", (char *)arg);
49. sleep(2);
50. value = (tsd_t *)pthread_getspecific(key);
51. if(value == NULL){
52. printf("no thread-specific data value was associated \
53. with key\n");
54. pthread_exit(NULL);
55. }
56. printf("%s done......\n", value->string);
57.}
58.
59.int main(int argc, char **argv)
60.{
61. pthread_t thread1, thread2;
62. int status;
63.
64. status = pthread_create(&thread1, NULL, thread_routine, "thread 1");
65. if(status != 0){
66. perror("pthread_create");
67. }
68.
69. status = pthread_create(&thread2, NULL, thread_routine, "thread 2");
70. if(status != 0){
71. perror("pthread_create");
72. }
73.
74. pthread_exit(NULL);
75.}
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
typedef struct tsd_tag{
pthread_t thread_id;
char *string;
}tsd_t;
pthread_key_t key;
pthread_once_t once = PTHREAD_ONCE_INIT;
void once_routine(void)
{
int status;
printf("Initializing key\n");
status = pthread_key_create(&key, NULL);
if(status != 0){
perror("pthread_key_create");
}
}
void *thread_routine(void *arg)
{
int status;
tsd_t *value = NULL;
status = pthread_once(&once, once_routine);
if(status != 0){
perror("pthread_once");
}
value = (tsd_t *)malloc(sizeof(tsd_t));
if(value == NULL){
perror("malloc");
}
status = pthread_setspecific(key, (void *)value);
if(status != 0){
perror("pthread_setspecific");
}
printf("%s set tsd value at %p\n", (char *)arg, value);
value->thread_id = pthread_self();
value->string = (char *)arg;
printf("%s starting......\n", (char *)arg);
sleep(2);
value = (tsd_t *)pthread_getspecific(key);
if(value == NULL){
printf("no thread-specific data value was associated \
with key\n");
pthread_exit(NULL);
}
printf("%s done......\n", value->string);
}
int main(int argc, char **argv)
{
pthread_t thread1, thread2;
int status;
status = pthread_create(&thread1, NULL, thread_routine, "thread 1");
if(status != 0){
perror("pthread_create");
}
status = pthread_create(&thread2, NULL, thread_routine, "thread 2");
if(status != 0){
perror("pthread_create");
}
pthread_exit(NULL);
}
運行結果:
[cpp] view plaincopyprint
01.huangcheng@ubuntu:~$ ./a.out
02.Initializing key
03.thread 2 set tsd value at 0x8fb7520
04.thread 2 starting......
05.thread 1 set tsd value at 0x8fb7530
06.thread 1 starting......
07.thread 2 done......
08.thread 1 done......
huangcheng@ubuntu:~$ ./a.out
Initializing key
thread 2 set tsd value at 0x8fb7520
thread 2 starting......
thread 1 set tsd value at 0x8fb7530
thread 1 starting......
thread 2 done......
thread 1 done......
示例代碼3:
[cpp] view plaincopyprint
01.#include <stdio.h>
02.#include <stdlib.h>
03.#include <pthread.h>
04.
05.pthread_key_t key;
06.
07.struct test_struct {
08. int i;
09. float k;
10.};
11.
12.
13.void *child1 (void *arg)
14.{
15. struct test_struct struct_data;
16.
17. struct_data.i = 10;
18. struct_data.k = 3.1415;
19.
20. pthread_setspecific (key, &struct_data);
21. printf ("結構體struct_data的地址為 0x%p\n", &(struct_data));
22. printf ("child1 中 pthread_getspecific(key)返回的指針為:0x%p\n", (struct test_struct *)pthread_getspecific(key));
23.
24. printf ("利用 pthread_getspecific(key)打印 child1 線程中與key關聯的結構體中成員值:\nstruct_data.i:%d\nstruct_data.k: %f\n", ((struct test_struct *)pthread_getspecific (key))->i, ((struct test_struct *)pthread_getspecific(key))->k);
25.
26. printf ("------------------------------------------------------\n");
27.}
28.
29.void *child2 (void *arg)
30.{
31. int temp = 20;
32. sleep (2);
33. printf ("child2 中變量 temp 的地址為 0x%p\n", &temp);
34. pthread_setspecific (key, &temp);
35. printf ("child2 中 pthread_getspecific(key)返回的指針為:0x%p\n", (int *)pthread_getspecific(key));
36. printf ("利用 pthread_getspecific(key)打印 child2 線程中與key關聯的整型變量temp 值:%d\n", *((int *)pthread_getspecific(key)));
37.}
38.
39.int main (void)
40.{
41. pthread_t tid1, tid2;
42.
43. pthread_key_create (&key, NULL);
44.
45. pthread_create (&tid1, NULL, (void *)child1, NULL);
46. pthread_create (&tid2, NULL, (void *)child2, NULL);
47. pthread_join (tid1, NULL);
48. pthread_join (tid2, NULL);
49.
50. pthread_key_delete (key);
51.
52. return (0);
53.}
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_key_t key;
struct test_struct {
int i;
float k;
};
void *child1 (void *arg)
{
struct test_struct struct_data;
struct_data.i = 10;
struct_data.k = 3.1415;
pthread_setspecific (key, &struct_data);
printf ("結構體struct_data的地址為 0x%p\n", &(struct_data));
printf ("child1 中 pthread_getspecific(key)返回的指針為:0x%p\n", (struct test_struct *)pthread_getspecific(key));
printf ("利用 pthread_getspecific(key)打印 child1 線程中與key關聯的結構體中成員值:\nstruct_data.i:%d\nstruct_data.k: %f\n", ((struct test_struct *)pthread_getspecific (key))->i, ((struct test_struct *)pthread_getspecific(key))->k);
printf ("------------------------------------------------------\n");
}
void *child2 (void *arg)
{
int temp = 20;
sleep (2);
printf ("child2 中變量 temp 的地址為 0x%p\n", &temp);
pthread_setspecific (key, &temp);
printf ("child2 中 pthread_getspecific(key)返回的指針為:0x%p\n", (int *)pthread_getspecific(key));
printf ("利用 pthread_getspecific(key)打印 child2 線程中與key關聯的整型變量temp 值:%d\n", *((int *)pthread_getspecific(key)));
}
int main (void)
{
pthread_t tid1, tid2;
pthread_key_create (&key, NULL);
pthread_create (&tid1, NULL, (void *)child1, NULL);
pthread_create (&tid2, NULL, (void *)child2, NULL);
pthread_join (tid1, NULL);
pthread_join (tid2, NULL);
pthread_key_delete (key);
return (0);
}
運行結果:
[cpp] view plaincopyprint
01.huangcheng@ubuntu:~$ ./a.out
02.結構體struct_data的地址為 0x0xb77db388
03.child1 中 pthread_getspecific(key)返回的指針為:0x0xb77db388
04.利用 pthread_getspecific(key)打印 child1 線程中與key關聯的結構體中成員值:
05.struct_data.i:10
06.struct_data.k: 3.141500
07.------------------------------------------------------
08.child2 中變量 temp 的地址為 0x0xb6fda38c
09.child2 中 pthread_getspecific(key)返回的指針為:0x0xb6fda38c
10.利用 pthread_getspecific(key)打印 child2 線程中與key關聯的整型變量temp 值:20