線程安全和可重入函數

一:什麼是線程安全?
就是所如果代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼,如果每次運行的結果是一樣的,而且其他變量的值和預期的是一樣的,就是線程安全的.或者說一個程序鎖提供的接口對於線程來說是原子操作或者多個線程之間的切換不會導致該接口執行結果的二義性,也就是我們不用考慮二義性.

下面這幾個條件可以判斷線程是不安全的:
1:函數中訪問全局變量和堆
2:函數中分配,重新分配釋放全局資源
3:函數中通過句柄和指針的不直接訪問
4:函數中使用了其他線程不安全的函數或者變量

因此編寫線程安全函數時,要注意兩點:
1:減少對臨界資源的依賴,儘量避免訪問全局變量,靜態變量或其他共享資源,如果必須要使用共享資源,所使用的地方必須加上互斥鎖保護
2:線程安全的函數所調用到函數也應該是線程安全的,如果所調用的函數不是線程安全的,那麼這些函數也必須被互斥鎖保護
如:下面這個函數是線程安全的,因爲函數不依賴任何全局變量

int sum(int a,int b)
{
return (a+b);
}

但是如何我們加上全局變量呢?
如:

static count =0;
void sumcount(int a,int b)
{
count++;
}
int sum(int x,int y)
{
sumcount();
return (x+y);
}

爲什麼呢?因爲調用的sumcount()函數是不是線程安全函數,該函數訪問未被保護的全局變量count.這樣的代碼在單線程可能沒有問題,在多線程就可能被併發修改.
解決的方法給全局變量加上互斥鎖保護

關於互斥鎖前面線程已經介紹詳情參考http://blog.csdn.net/f2016913/article/details/72885828線程死鎖

二:可重入函數
概念
1:重入:函數被不同的控制流程調用,有可能在第一次調用還沒有返回時就再次進入該函數,稱爲重入.
2:可重入:一個可重入的函數簡單來說就是可以被中斷的函數,也就是說,可以在這個函數執行的任何時刻中斷它,轉入OS調度下去執行另外一段代碼,而返回控制時不會出現什麼錯誤;
3:不可重入:而不可重入的函數由於使用了一些系統資源,比如全局變量區,中斷向量表等,所以它如果被中斷的話,可能會出現問題,這類函數是不能運行在多任務環境下的。
注意:
可重入函數可以在任何時刻被中斷,稍後再繼續運行,不會丟失數據,不可重入函數不能由超過一個任務所共享,除非能確保函數的互斥.

驗證不可重入函數

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
int count =0;
void fun()
{
    int i =5;
    while((i--)>0)
          {
           count++;
              printf("count is %d\n",count);
              sleep(1);//每隔一秒打印一次
          }    
}

 int main()
 {
     signal(2,fun);//收到2號信號,執行自定義處理動作
     fun();
     printf("main-> count:%d\n",count);
     return 0;
 }

這裏寫圖片描述
通過結果我們知道,當執行到中斷處理程序時,繼續運行,不可重入函數會導致數據的丟失.
如何把不可重入函數變成可重入函數.就是把全局變量該成局部變量即可.

void fun()
{
int count =0;
    int i =5;
    while((i--)>0)
          {
           count++;
              printf("count is %d\n",count);
              sleep(1);//每隔一秒打印一次
          }    
}

 int main()
 {
     signal(2,fun);//收到2號信號,執行自定義處理動作
     fun();
     return 0;
 }

這裏寫圖片描述
這樣fun函數就從不可重入函數變成可重入函數了.
可重入函數滿足的條件:
a:不能使用靜態變量或者全局變量
b:不能使用malloc或者new開闢的空間(堆)
c:不能調用不可重入函數
d:不返回靜態或全局數據,所有數據都是由函數的調用者提供
e:不能依賴於單一資源的鎖
不可重入的條件:
1:調用了malloc/free函數,因爲malloc函數是用全局鏈表來管理的
2:調用標準I/O庫函數,標準I/O庫很多實現都以不可重入的方式使用全局數據結構
3:可重入體內使用了靜態的數據結構
三:線程安全和可重入函數的區別和聯繫
(1):線程安全是多線程情況纔會引發的,而可重入函數只有在在單線程的情況下發生
(2):線程安全不一定是可重入函數的,而可重入函數一定是線程安全的
(3):如果一個函數有全局變量,則這個函數既不是線程安全也不是可重入的
(4):如果將對臨界資源的訪問加上鎖,則這個函數是線程安全的,但是如果這個重入函數若鎖還未釋放則會產生死鎖,因此是不可重入的
(5):線程安全函數能夠使不同的線程訪問同一塊地址空間,而不可重入函數要求不同的執行流堆數據的操作互不影響使結果是相同的.
我們可以用圖來表示他們之間的差別:
這裏寫圖片描述

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