LINUX線程之一次性初始化(PTHREAD_ONCE)


 

1.一次性初始化 
在 Linux函數列表 中描述了Linux線程中的常用函數,這里詳細講解 pthread_once 函數的功能和使用。

(1)為何有“一次性初始化概念”出現? 
其實在開發中,很多事情都僅僅需要做一次,不管是什么。在主函數中並且在調用任何其他依賴於初始化的事物之前初始化應用是最為容易的,特別是在創建任何線程之前初始化它需要的數據,如 互斥量、條件變量、線程特定數據鍵等。

(2)靜態初始化變量也很方便,為何pthread_onc 會出現? 
Linux線程開發中,通常對於 互斥量、條件變量等都會提供兩種初始化方式,分別是動態初始化和靜態初始化。如:

pthread_cond_t cond; 
//動態初始化: pthread_cond_init(&cond, NULL); //靜態初始化: pthread_cond_t = PTHREAD_COND_INITIALIZER; pthread_mutex_t; //動態初始化: pthread_mutex_init(&mutex,NULL); //靜態初始化: pthread_mutex_t = PTHREAD_MUTEX_INITIALIZER;


這里之所以出現 pthread_once 的主要原因是因為之前的 Linux Phread 不支持靜態地初始化一個互斥量、條件變量等 。這樣,要使用一個互斥量,就不得不首先調用pthread_mutex_init 初始化互斥量。而且必須僅僅初始化互斥量一次,因此初始化調用應在一次性初始化代碼中進程。pthread_once解決了這個遞歸的問題。當互斥量的靜態初始化被加到標准中時,pthread_once作為便利功能而被保留了下來。若使用pthread_once方便,就使用它,但是不必一定要使用它。

(3)pthread_once 如何使用? 
首先,來看看 pthread_once 的函數原型: 
這里寫圖片描述

/*確保初始化函數INIT_ROUTINE只被調用一次,即使pthread_once使用相同的ONCE_CONTROL *參數執行了多次.ONCE_CONTROL必須指向一個初始化為PTHREAD_ONCE_INIT的靜態或外部變量. *初始化函數可能會拋出異常,這就是為什么這個函數沒有被標記為__THROW. */ extern int pthread_once (pthread_once_t *__once_control, void (*__init_routine) (void)) __nonnull ((1, 2));


根據該函數的定義: 
①首先,需要聲明類型為 pthread_once_t 的一個控制變量,而且該控制變量必須使用PTHREAD_ONCE_INIT宏來靜態的初始化。 
②必須創建一個包含與該 “控制變量(pthread_once_t)” 相關聯的所有初始化代碼函數,現在線程可以在任何時間調用pthread_once,指定一個指向一個控制變量的指針和指向相關初始化函數的指針。 

pthread_once函數首先檢查控制變量,以判斷是否已經完成初始化。如果已經完成,pthread_once簡單的返回;否則,它回去調用初始化函數(沒有參數),並且記錄下初始化被完成。如果在一個線程初始化的時候,另外一個線程也調用了pthread_once,則調用線程將阻塞等待,知道那個線程完成初始化后返回。換言之,當調用pthread_once成功返回時,調用總是能夠肯定所有的狀態已經初始化完成。

代碼1 
pthread_once初始化函數 threadOnceInit的主要功能是初始化互斥量。pthread_once的使用能夠確保它只被初始化一次。 
線程執行函數subThread中,在調用互斥量之前,先去調用了pthread_once,是為了保證它即使在主函數中沒有被創建,它也會存在。

/************************************************************************* * File Name: pthread_once.c * Author: The answer * Function: Other * Mail: 2412799512@qq.com * Created Time: 2018年09月09日 星期日12時06分09秒 ************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <assert.h> #include <time.h> #include <pthread.h> #include <sys/select.h> #include <sys/epoll.h> #include <sys/poll.h> #define STRUCT_INIT(l,r) .l = r #define CHECK_VARIABLE(l,r) do{if(0 != r){fprintf(stderr,"[%s], err:[%d]\n",l,r);break;}}while(0); //宏初始化pthread_once_t控制變量 pthread_once_t once = PTHREAD_ONCE_INIT; pthread_mutex_t mutex ; static const int globalVal = 10; //pthread_once初始化函數 void threadOnceInit(){ int ret = -1; ret = pthread_mutex_init(&mutex, NULL); CHECK_VARIABLE("pthread_mutex_init error",ret); puts("pthread_mutex_init success."); return; } //子線程執行函數 void* subThread(void* param){ printf("subThread to do...\n"); int ret = -1; ret = pthread_once(&once,threadOnceInit); CHECK_VARIABLE("pthread_once error",ret); ret = pthread_mutex_lock(&mutex); CHECK_VARIABLE("pthread_mutex_lock err",ret); printf("subThread has clocked the mutex. and globalVal is: [%d]\n",globalVal); ret = pthread_mutex_unlock(&mutex); CHECK_VARIABLE("pthread_mutex_unlock err.",ret); return NULL; } int main(int argc,char **argv) { pthread_t Tid; int ret = -1; ret = pthread_create(&Tid,NULL,subThread, NULL); CHECK_VARIABLE("pthread_create err.",ret); ret = pthread_once(&once,threadOnceInit); CHECK_VARIABLE("pthread_once err",ret); ret = pthread_mutex_lock(&mutex); CHECK_VARIABLE("pthread_mutex_lock",ret); printf("Main func has locked the mutex.\n"); ret = pthread_mutex_unlock(&mutex); CHECK_VARIABLE("pthread_mutex_unlock",ret); ret = pthread_join(Tid,NULL); CHECK_VARIABLE("pthread_join",ret); return 0; } 

編譯:gcc pthread_once.c -o a -lpthread 
執行:./a 
其結果為:

pthread_mutex_init success.
Main func has locked the mutex. subThread to do... subThread has clocked the mutex. and globalVal is: [10]

通過其打印結果可以看到:主函數先於子線程先執行。主函數中先調用pthread_once,此時mutex互斥量成功的被初始化,然后子線程中再去調用pthread_once時候,檢測到已經完成了初始化操作,則立刻返回不執行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM