線程安全與可重入函數

一,什麼是線程安全?
所謂的線程安全是指在多線程中,即使有多個進程訪問同一份代碼也不會造成不確定的結果。
即:多個線程完成一個任務與一個線程去完成的結果總是一樣的,並且不會產生預料之外的結果,那麼這樣的線程便是安全的。
二,怎麼保證線程是安全的?
對於多線程的程序訪問衝突的問題十分常見,而最簡單的方法便是引入互斥鎖,通過互斥鎖可以實現拿到鎖的線程可以正常執行,而沒有拿到的,只有等待,不能訪問共享資源,直到鎖被釋放。
eg:請看如下代碼:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
int count=0;
void* myPthread1(void)
{
 int i=0;
 int val=0;
 for(;i<5000;i++)
 {
  val=count;
  printf("pthread id=%ld,count=:%d\n",(unsigned long)pthread_self(),count);
  count=val+1;
 }
}

int main()
  {
   pthread_t p1,p2;
   pthread_create(&p1,NULL,(void*)myPthread1,NULL);
   pthread_create(&p2,NULL,(void*)myPthread1,NULL);
   pthread_join(p1,NULL);
   pthread_join(p2,NULL);
   printf("count=%d\n",count);
   return 0;

  }

pthead_create()函數是用來建立一個線程的,如上可得我們創建了兩個線程分別爲p1和p2,然後用這兩個線程分別計算從0加到5000的值,很明顯最後答案的輸出count會是10000,那麼實際情況呢?
如下是Linux下運行得到的結果
第一次,
這裏寫圖片描述
第二次:
這裏寫圖片描述
其實不管試多少次我們都會發現結果很少會是正確,而這便是線程安全的問題了,當兩個線程不加限制的去訪問val時可能拿到的並不是當前正確的值,例如當a線程剛拿到val時,而b線程可能正在拿之前val進行計算,因此最後便導致了結果的不確定性。
爲了解決問題,我們加入互斥鎖,
如下爲改造後的代碼:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
int count=0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //申請一個公共鎖 
void* myPthread1(void)
{
 int i=0;
 int val=0;
 for(;i<5000;i++)
 {
  //在計算時,先加鎖 
  pthread_mutex_lock(&mutex);
  val=count;
  printf("pthread id=%lld,count=:%d\n",(unsigned long long)pthread_self(),count);
  count=val+1;
   pthread_mutex_unlock(&mutex);
  //訪問完畢後,釋放鎖 
 }
}

int main()
  {
   pthread_t p1,p2;
   pthread_create(&p1,NULL,(void*)myPthread1,NULL);
   pthread_create(&p2,NULL,(void*)myPthread1,NULL);
   pthread_join(p1,NULL);  //線程的等待
   pthread_join(p2,NULL);
   printf("count=%d\n",count);
   return 0;

  }

如下爲改造後的結果:
3:
這裏寫圖片描述
4:
這裏寫圖片描述

通過加鎖的方式,現在便可以實現,線程每次的操作都會是原子的,所以結果將會一直是10000.
三,什麼是可重入函數?
可重入函數:
在 實時系統的設計中,經常會出現多個任務調用同一個函數的情況。如果這個函數不幸被設計成爲不可重入的函數的話,那麼不同任務調用這個函數時可能修改其他任 務調用這個函數的數據,從而導致不可預料的後果。那麼什麼是可重入函數呢?所謂可重入是指一個可以被多個任務調用的過程,任務在調用時不必擔心數據是否會 出錯。不可重入函數在實時系統設計中被視爲不安全函數。
滿足下列條件的函數多數是不可重入的:
(1)函數體內使用了靜態的數據結構;
(2)函數體內調用了malloc()或者free()函數;
(3)函數體內調用了標準I/O函數。
如何寫出可重入的函數?
在函數體內不訪問那些全局變量,不使用靜態局部變量,堅持只使用缺省態(auto)局部變量,寫出的函數就將是可重入的。如果必須訪問全局變量,記住利用互斥信號量來保護全局變量。或者調用該函數前關中斷,調用後再開中斷。
可重入函數可以被一個以上的任務調用,而不必擔心數據被破壞。可重入函數任何時候都可以被中斷,一段時間以後又可以運行,而相應的數據不會丟失。可重入函數或者只使用局部變量,即保存在CPU寄存器中或堆棧中;或者使用全局變量,則要對全局變量予以保護。
四,線程安全與可重入函數的對比
可重入函數與線程安全的區別與聯繫:
(1)線程安全是在多個線程情況下引發的,而可重入函數可以在只有一個線程的情況下。
(2)線程安全不一定是可重入的,而可重入函數則一定是線程安全的。
(3)如果一個函數中有全局變量,那麼這個函數既不是線程安全也不是可重入的。
(4)如果將對臨界資源的訪問加上鎖,則這個函數是線程安全的,但如果這個重入函數若鎖還未釋放則會產生死鎖,因此是不可重入的。
(5)線程安全函數能夠使不同的線程訪問同一塊地址空間,而可重入函數要求不同的執行流對數據的操作互不影響使結果是相同的。

發佈了34 篇原創文章 · 獲贊 8 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章