天天看點

Linux系統程式設計——線程私有資料

在多線程程式中,經常要用全局變量來實作多個函數間的資料共享。由于資料空間是共享的,是以全局變量也為所有線程共有。

測試代碼如下:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

int key = 100; //全局變量

void *helloworld_one(void *arg)
{
  printf("the message is %s\n",(char *)arg);
  key = 10;
  printf("key=%d, the child id is %lu\n", key, pthread_self());
  
  return NULL;
}

void *helloworld_two(void *arg)
{
  printf("the message is %s\n", (char *)arg);
  sleep(1);
  printf("key=%d, the child id is %lu\n", key, pthread_self());
  
  return NULL;
}

int main(int argc, char *argv[])
{
  pthread_t thread_id_one;
  pthread_t thread_id_two;

  //建立線程
  pthread_create(&thread_id_one, NULL, helloworld_one, "helloworld_one");
  pthread_create(&thread_id_two, NULL, helloworld_two, "helloworld_two");
  
  //等待線程結束,回收資源
  pthread_join(thread_id_one, NULL);
  pthread_join(thread_id_two, NULL);
  
  return 0;
}      

運作結果如下:

Linux系統程式設計——線程私有資料

由運作結果可以看出,其中一個線程對全局變量的修改将影響到另一個線程的通路。

但有時應用程式設計中必要提供線程私有的全局變量,這個變量僅線上程中有效,但卻可以跨過多個函數通路。比如在程式裡可能需要每個線程維護一個連結清單,而會使用相同的函數來操作這個連結清單,最簡單的方法就是使用同名而不同變量位址的線程相關資料結構。這樣的資料結構可以由 Posix 線程庫維護,成為線程私有資料 (Thread-specific Data,或稱為 TSD)。

下面接口所需頭檔案:

#include <pthread.h> 

1)建立線程私有資料

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

功能:

建立一個類型為 pthread_key_t 類型的私有資料變量( key )。

參數:

key:在配置設定( malloc )線程私有資料之前,需要建立和線程私有資料相關聯的鍵( key ),這個鍵的功能是獲得對線程私有資料的通路權。

destructor:清理函數名字( 如:fun )。當線程退出時,如果線程私有資料位址不是非 NULL,此函數會自動被調用。該函數指針可以設成 NULL ,這樣系統将調用預設的清理函數。

回調函數其定義如下:

void fun(void *arg)

{

// arg 為 key 值

}

傳回值:

成功:0

失敗:非 0

不論哪個線程調用 pthread_key_create(),所建立的 key 都是所有線程可通路,但各個線程可根據自己的需要往 key 中填入不同的值,相當于提供了一個同名不同值的變量。

2)登出線程私有資料

int pthread_key_delete(pthread_key_t key);

功能:

登出線程私有資料。這個函數并不會檢查目前是否有線程正使用線程私有資料( key ),也不會調用清理函數 destructor() ,而隻是将線程私有資料( key )釋放以供下一次調用 pthread_key_create() 使用。

參數:

key:待登出的私有資料。

傳回值:

成功:0

失敗:非 0

3)設定線程私有資料的關聯

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

功能:

設定線程私有資料( key ) 和 value 關聯,注意,是 value 的值(不是所指的内容)和 key 相關聯。

參數:

key:線程私有資料。

value:和 key 相關聯的指針。

傳回值:

成功:0

失敗:非 0

4)讀取線程私有資料所關聯的值

void *pthread_getspecific(pthread_key_t key);

功能:

讀取線程私有資料( key )所關聯的值。

參數:

key:線程私有資料。

傳回值:

成功:線程私有資料( key )所關聯的值。

失敗:NULL

示例代碼如下:

// this is the test code for pthread_key 
#include <stdio.h> 
#include <pthread.h> 

pthread_key_t key;  // 私有資料,全局變量

void echomsg(void *t) 
{ 
  printf("[destructor] thread_id = %lu, param = %p\n", pthread_self(), t); 
} 

void *child1(void *arg) 
{ 
  int i = 10;
  
  pthread_t tid = pthread_self(); //線程号
  printf("\nset key value %d in thread %lu\n", i, tid); 
  
  pthread_setspecific(key, &i); // 設定私有資料
  
  printf("thread one sleep 2 until thread two finish\n\n");
  sleep(2); 
  printf("\nthread %lu returns %d, add is %p\n",
    tid, *((int *)pthread_getspecific(key)), pthread_getspecific(key) ); 
} 

void *child2(void *arg) 
{ 
  int temp = 20;
  
  pthread_t tid = pthread_self();  //線程号
  printf("\nset key value %d in thread %lu\n", temp, tid); 
  
  pthread_setspecific(key, &temp); //設定私有資料
  
  sleep(1); 
  printf("thread %lu returns %d, add is %p\n", 
    tid, *((int *)pthread_getspecific(key)), pthread_getspecific(key)); 
} 

int main(void) 
{ 
  pthread_t tid1,tid2; 
  pthread_key_create(&key, echomsg); // 建立
  
  pthread_create(&tid1, NULL, child1, NULL); 
  pthread_create(&tid2, NULL, child2, NULL); 
  pthread_join(tid1, NULL);
  pthread_join(tid2, NULL);
  
  pthread_key_delete(key); // 登出
  
  return 0; 
}      

運作結果如下

Linux系統程式設計——線程私有資料

繼續閱讀