線程安全
在多線程編程中,線程安全是必須要考慮的因素。
什么是線程安全?
在多線程環(huán)境中,多個線程在同一時刻對同一份資源進(jìn)行寫操作時,不會出現(xiàn)數(shù)據(jù)不一致。反之,則是線程非安全的。
線程安全是程序設(shè)計中的術(shù)語,指某個函數(shù)、函數(shù)庫在多線程環(huán)境中被調(diào)用時,能夠正確地處理多個線程之間的公用變量,使程序功能正確完成。
為了確保在多線程環(huán)境中的線程安全,就要確保數(shù)據(jù)的一致性。確保線程安全的幾種方法:
使用互斥鎖
一個線程,如果需要訪問公共資源,需要獲得互斥鎖并對其加鎖,資源在在鎖定過程中,如果其它線程對其進(jìn)行訪問,也需要獲得互斥鎖,如果獲取不到,線程只能進(jìn)行阻塞,直到獲得該鎖的線程解鎖。
#include
int increment_counter(void)
{
static int counter = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex);
// only allow one thread to increment at a time
++counter;
// store value before any other threads increment it further
int result = counter;
pthread_mutex_unlock(&mutex);
return result;
}
這個函數(shù)是線程安全的,可以在多個線程中被調(diào)用。
使用原子操作
上面的例子中,使用一個 互斥鎖來保護(hù)一次簡單的增量操作顯然過于昂貴,我們可以使用一些專門的原子操作API函數(shù)來替代。如上述例子,c++11中的原子變量提供了一個可使此函數(shù)既線程安全又可重入(而且還更簡潔)的替代方案:
#include
int increment_counter(void)
{
static std::atomic<int> counter(0);
// increment is guaranteed to be done atomically
int result = ++counter;
return result;
}
Linux內(nèi)核中原子整形操作:
#include
int increment_counter(void)
{
atomic_t counter = ATOMIC_INIT(0);
// increment is guaranteed to be done atomically
atomic_inc(&counter);
int result = counter;
return result;
}
什么是原子操作?
從字面上簡單理解,原子是一種很微小的粒子;原子操作是不能再進(jìn)一步細(xì)分的操作。
從上面互斥鎖的例子來看,在線程層面,線程1和線程2同時調(diào)用了increment_counter函數(shù),被 mutex 保護(hù)的操作是原子操作,lock、unlock及保護(hù)部分要整體順序運(yùn)行,不可再進(jìn)一步細(xì)分,作為一個原子存在 。
如果確定某個操作是原子的,并且有原子操作API函數(shù)可以使用,就不用為了去保護(hù)這個操作而加上會耗費昂貴性能開銷的鎖。
如,Linux內(nèi)核原子整形操作 API 函數(shù)表(來源:正點原子) :
防止過度優(yōu)化
線程安全的函數(shù)應(yīng)該為每個調(diào)用它的線程分配專門的空間,把多個線程共享的變量正確對待(如,通知編譯器該變量為“易失(volatile)”型,阻止其進(jìn)行一些不恰當(dāng)?shù)膬?yōu)化)。
線程安全函數(shù)與可重入函數(shù)?
先明確概念:
- 線程安全函數(shù):能夠正確地處理多個線程之間的公用變量的函數(shù)。、
- 可重入函數(shù):在任意時刻被中斷然后操作系統(tǒng)調(diào)度執(zhí)行另一段代碼,這段代碼又使用了該副程序不會出錯。
可重入函數(shù)應(yīng)當(dāng)滿足條件:
- 不能含有靜態(tài)(全局)非常量數(shù)據(jù)。
- 不能返回靜態(tài)(全局)非常量數(shù)據(jù)的地址。
- 只能處理由調(diào)用者提供的數(shù)據(jù)。
- 不能依賴于單例模式資源的鎖。
- 調(diào)用(call)的函數(shù)也必需是可重入的。
可重入函數(shù)未必是線程安全的;線程安全函數(shù)未必是可重入的。
例子1:上述例子中的increment_counter函數(shù)是線程安全的,但是并不是可重入的。因為使用了互斥鎖,如果這個函數(shù)用在可重入的中斷處理程序中,如果在pthread_mutex_lock(&mutex)和pthread_mutex_unlock(&mutex)之間產(chǎn)生另一個調(diào)用函數(shù)increment_counter的中斷,則會第二次執(zhí)行此函數(shù),此時由于mutex已被lock,函數(shù)會在pthread_mutex_lock(&mutex)處阻塞,并且由于mutex沒有機(jī)會被unlock,阻塞會永遠(yuǎn)持續(xù)下去。
例子2:一個函數(shù)打開某個文件并讀入數(shù)據(jù)。這個函數(shù)是可重入的,因為它的多個實例同時執(zhí)行不會造成沖突;但它不是線程安全的,因為在它讀入文件時可能有別的線程正在修改該文件,為了線程安全必須對文件加“同步鎖”。
-
編譯器
+關(guān)注
關(guān)注
1文章
1642瀏覽量
49312 -
C++語言
+關(guān)注
關(guān)注
0文章
147瀏覽量
7037 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21749
發(fā)布評論請先 登錄
相關(guān)推薦
不同創(chuàng)建線程安全Set的方式
調(diào)用非安全線程的dll的問題
XC32源碼和字符串線程安全
Linux下的線程安全是什么
什么是線程安全?如何去實現(xiàn)線程安全?
什么是線程安全
解決線程安全問題技巧匯總
java的線程安全、單例模式、JVM內(nèi)存結(jié)構(gòu)
什么是線程安全 如何實現(xiàn)線程安全代碼
如何知道你的代碼是否線程安全
![如何知道你的代碼是否<b class='flag-5'>線程</b><b class='flag-5'>安全</b>](https://file1.elecfans.com/web2/M00/AD/94/wKgZomVByJqATZOjAAGqxiwwwzU143.jpg)
評論