Linux 下C語言多線程編程--線程數據

線程數據

  • 在C語言編程中,我們都知道,數據共享往往是通過全局變量的方式,全局變量在單線程中不會有什麼問題,但是在多線程情況下,如果多個線程都對這個全局變量進行讀寫操作,需要加鎖進行保護,鎖的代價其實是很大的。
  • 在實際的使用中,很多時候我們只想要線程之間的全局變量,只能在該線程內訪問,其他線程不能訪問,這個時候就可以使用線程數據來實現,通過一個鍵值pthread_key_t來實現

鍵值的創建

函數原型:

/* Create a key value identifying a location in the thread-specific
   data area.  Each thread maintains a distinct thread-specific data
   area.  DESTR_FUNCTION, if non-NULL, is called with the value
   associated to that key when the key is destroyed.
   DESTR_FUNCTION is not called if the value associated is NULL when
   the key is destroyed.  */
extern int pthread_key_create (pthread_key_t *__key,
			       void (*__destr_function) (void *))
     __THROW __nonnull ((1));

第一個參數:就是一個鍵值的指針
第二個參數:一個destructor函數,可以傳NULL,如果不爲空,當每個線程結束時,調用這個函數來釋放綁定在這個鍵值上的內存快(這個參數很厲害,Demo中有使用說明)。

注:值得注意的是,這個函數往往和pthread_once()函數一起使用,目的是讓這個鍵值只被創建一次

/* Guarantee that the initialization function INIT_ROUTINE will be called
   only once, even if pthread_once is executed several times with the
   same ONCE_CONTROL argument. ONCE_CONTROL must point to a static or
   extern variable initialized to PTHREAD_ONCE_INIT.

   The initialization functions might throw exception which is why
   this function is not marked with __THROW.  */
extern int pthread_once (pthread_once_t *__once_control,
			 void (*__init_routine) (void)) __nonnull ((1, 2));

第一個參數:是和key對應的一個標誌,初始化爲:PTHREAD_ONCE_INIT
第二個參數:初始化函數

鍵值的綁定和獲取

綁定函數:

/* Store POINTER in the thread-specific data slot identified by KEY. */
extern int pthread_setspecific (pthread_key_t __key,
				const void *__pointer) __THROW ;

第一個參數:鍵值key
第二個參數:指向想要綁定的線程數據的指針

獲取函數:

/* Return current value of the thread-specific data slot identified by KEY.  */
extern void *pthread_getspecific (pthread_key_t __key) __THROW;

參數就是key,返回綁定的線程數據指針

Demo

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

//聲明一個全局變量key,該key對所有線程可見,
//但是該key具體指向的內容各個線程不一樣
//可以理解爲一個二級指針
pthread_key_t key;
//用於標識key,key只創建一次
pthread_once_t once_create = PTHREAD_ONCE_INIT;

//自定義數據結構,用於測試
typedef struct ThreadData_t {
    pthread_t m_pid;
    char* m_threadName;
} ThreadData;

//釋放線程自定義數據,即每個線程使用key具體指向的數據
//這裏其實就是ThreadData
static void free_thread_data(void* pData) {
    fprintf(stderr, "%s():addr= %p\n", __FUNCTION__, pData);
    free(pData);
}

//創建key
static void create_my_key(void) {
    fprintf(stderr, "%s():called\n", __FUNCTION__);
    pthread_key_create(&key, free_thread_data);
}

static void test_get_thread_data(void) {
    ThreadData* pData = (ThreadData*)pthread_getspecific(key);
    fprintf(stderr, "%s():%s get data by key addr=%p\n",
            __FUNCTION__, pData->m_threadName, pData);
    return;
}

//線程函數routine
static void* test_thread(void* thread_name) {
    //如果key沒有被創建,就創建key,被創建了就不會再創建
    pthread_once(&once_create, create_my_key);

    //申請一個線程數據結構,該數據只在該線程內可見,其他線程不可見,
    //但是該線程的其他函數可以獲取到該數據
    ThreadData* pData = (ThreadData*)malloc(sizeof(ThreadData));
    if (pData == NULL) {
        fprintf(stderr, "%s():not enough memory\n", __FUNCTION__);
        return NULL;
    }

    pData->m_pid = pthread_self();
    pData->m_threadName = (char*)thread_name;
    fprintf(stderr, "thread %s malloc data addr=%p\n",
            pData->m_threadName, pData);

    pthread_setspecific(key, pData);
    sleep(5);
    test_get_thread_data();
    return NULL;
}

int main(int argc, char** argv) {
    pthread_t pid1, pid2;

    if (pthread_create(&pid1, NULL, test_thread, (void*)"test thread1") != 0) {
        fprintf(stderr, "create thread1 failed\n");
        return -1;
    }
    if (pthread_create(&pid2, NULL, test_thread, (void*)"test thread2") != 0) {
        fprintf(stderr, "create thread2 failed\n");
        return -1;
    }
    pthread_join(pid1, NULL);
    pthread_join(pid2, NULL);

    pthread_key_delete(key);
    return 0;
}

執行結果

編譯:gcc main.c -o main -lpthread
Demo結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章