溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何分析Linux多線程可重入函數(shù)

發(fā)布時間:2022-01-27 10:44:21 來源:億速云 閱讀:225 作者:柒染 欄目:開發(fā)技術(shù)

如何分析Linux多線程可重入函數(shù),相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

Reentrant和Thread-safe

在單線程程序中,整個程序都是順序執(zhí)行的,一個函數(shù)在同一時刻只能被一個函數(shù)調(diào)用,但在多線程中,由于并發(fā)性,一個函數(shù)可能同時被多個函數(shù)調(diào)用,此時這個函數(shù)就成了臨界資源,很容易造成調(diào)用函數(shù)處理結(jié)果的相互影響,如果一個函數(shù)在多線程并發(fā)的環(huán)境中每次被調(diào)用產(chǎn)生的結(jié)果是不確定的,我們就說這個函數(shù)是”不可重入的”/”線程不安全”的。為了解決這個問題,POSIX多線程庫提出了一種機(jī)制,用來解決多線程環(huán)境中的線程數(shù)據(jù)私有化問題,這套機(jī)制的主要思想是利用同步和互斥維護(hù)一個同名不同值的表,這個表會維護(hù)每個線程自己的資源地址,表面上是同一個變量,實(shí)質(zhì)上這個變量在不同的線程中的地址是不一樣,這樣就保證了每個線程其實(shí)都在使用自己的資源,實(shí)現(xiàn)了”thread-safe”。

其實(shí),隨著多線程程序的逐漸流行,除了這種利用系統(tǒng)機(jī)制保護(hù)線程私有數(shù)據(jù)的方法,還有一部分人重新編寫了一些多線程庫函數(shù),這些函數(shù)的主要特點(diǎn)就是實(shí)現(xiàn)了算法和數(shù)據(jù)的分離,函數(shù)內(nèi)部只負(fù)責(zé)實(shí)現(xiàn)算法,需要的數(shù)據(jù)由線程傳入,這樣就保證了函數(shù)的多線程安全,

eg

 char *asctime(const struct tm *tm);
 char *asctime_r(const struct tm *tm, char *buf);  //這個就是asctime的thread-safe版,有_r后綴

但由于接口不同,完全重寫的函數(shù)推廣尚需時日。

當(dāng)下用的更多的是使用_REENTRANT來在原來的函數(shù)的基礎(chǔ)上改造,如果編譯的時候定義了這個宏,相關(guān)的庫函數(shù)就會被編譯成”thread-safe”的版本。

模型

如果要查看這些函數(shù)的man手冊,可以安裝相關(guān)的man手冊

 pthread_key_t key           //創(chuàng)建用于保護(hù)線程私有資源的
 keypthread_once_t once_key     //創(chuàng)建用于初始化key的once_key,要求用PTHREAD_INIT_ONCE來賦值,否則結(jié)果不確定
 
 pthread_key_create()        //創(chuàng)建
 keypthread_once()              //初始化
 keypthread_getspedifc()        //從key表中獲得線程私有資源的地址
 
 pthread_setspedifc()        //將線程私有資源的地址放到key中...

例子

表面上每個函數(shù)調(diào)用了reverse()都會得到rev的地址,其實(shí)這個rev地址在不同的線程中并不相同,一旦一個線程調(diào)用了reverse()函數(shù),函數(shù)首先會到key標(biāo)識的表中去搜索這個線程以前是否調(diào)用過這個函數(shù),如果調(diào)用過,就將表中屬于這個線程的rev地址返回,如果沒有,就分配rev,并將該線程和它的專屬rev地址注冊到表中,這樣就把reverse()打造成了一個可重入的函數(shù)。

 #include #include #include #include 
 pthread_key_t key;
 pthread_once_t once_key=PTHREAD_ONCE_INIT;
 
 #ifdef _REENTRANT
 void myDestructor(void*p){
     free(p);
 }
 void myCreateKey(void){    //創(chuàng)建key
     pthread_key_create(&key,myDestructor);
 }
 #endif
 
 char* reverse(char* buf,int len){
 #ifdef _REENTRANT
     //初始化key
     pthread_once(&once_key,myCreateKey);  //從key中獲取一個thread-specific的數(shù)據(jù)
     char* rev=(char*)pthread_getspecific(key);
     if(NULL==rev){
         rev=(char*)malloc(len+1);        //將thread-specific的數(shù)據(jù)放到key中
         pthread_setspecific(key,rev);
     }
 #else
     static char rev[100];
 #endif
     bzero(rev,sizeof(rev));    //翻轉(zhuǎn)buf
     while(len--)
         rev[len]=*buf++;
     return rev;
 }
 void* fcn1(void* p){
     while(1){
         char buf[100]="123456789";
         printf("[%lu]:%s\n",pthread_self(),buf);
         char* rev=reverse(buf,strlen(buf));
         sleep(1);
         printf("[%lu]:%s\n",pthread_self(),rev);
     }
 
 }
 void* fcn2(void* p){
     while(1){
         char buf[100]="abcdef";
         printf("[%lu]:%s\n",pthread_self(),buf);        
         char* rev=reverse(buf,strlen(buf));
         sleep(2);        
         printf("[%lu]:%s\n",pthread_self(),rev);
 
     }
 }int main(int argc, const char *argv[]){
     pthread_t tid[4];
     pthread_create(&tid[0],NULL,fcn1,NULL);
     pthread_create(&tid[1],NULL,fcn2,NULL);
     pause();    
     return 0;
 }

看完上述內(nèi)容,你們掌握如何分析Linux多線程可重入函數(shù)的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI