C++中關(guān)于互斥量的全面認(rèn)知
互斥量(保護(hù)對共享變量的訪問)
1.概念
互斥(mutex)是防止同時(shí)訪問共享資源的程序?qū)ο蟆?/p>
為避免線程更新共享變量時(shí)所出現(xiàn)問題,必須使用互斥量( mutex 是 mutual exclusion 的 縮寫)來確保同時(shí)僅有一個(gè)線程可以訪問某項(xiàng)共享資源。 即就是 使用互斥量來實(shí)現(xiàn)原子訪問操作
2.狀態(tài)
已鎖定( locked)和未鎖定( unlocked)。任何時(shí)候,至多只有一個(gè)線程可以鎖定該互斥量。試圖對已經(jīng)鎖定的某一互斥量再次加鎖,將可能阻塞線程或者報(bào)錯(cuò)失敗,具體取決于加鎖時(shí)使用的方法
3.特點(diǎn)
一旦線程鎖定互斥量,隨即成為該互斥量的所有者。只有所有者才能給互斥量解鎖。因?yàn)樗袡?quán)的關(guān)系,有時(shí)會使用術(shù)語獲取( acquire)和釋放( release)來替代加鎖和解鎖。
互斥量的分配
互斥量既可以像靜態(tài)變量那樣分配,也可以在運(yùn)行時(shí)動態(tài)創(chuàng)建
1.靜態(tài)分配
互斥量是屬于 pthread_mutex_t 類型的變量
在使用之前必須對其初始化。
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER
2.動態(tài)分配
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
參數(shù) mutex 指定函數(shù)執(zhí)行初始化操作的目標(biāo)互斥量。
參數(shù) attr 是指向 pthread_mutexattr_t 類型對象的指針,該對象在函數(shù)調(diào)用之前已經(jīng)過了初始化處理,用于定義互斥量的屬性。若將 attr 參數(shù)置為 NULL,則該互斥量的各種屬性會取默認(rèn)值。
注:
- 初始化一個(gè)業(yè)已初始化的互斥量將導(dǎo)致未定義的行為
- 動態(tài)分配于堆中的互斥量。例如,動態(tài)創(chuàng)建針對某一結(jié)構(gòu)的鏈表,表中每個(gè)結(jié)構(gòu)都包含一個(gè) pthread_mutex_t 類型的字段來存放互斥量,借以保護(hù)對該結(jié)構(gòu)的訪問。
- 互斥量是在棧中分配的自動變量。
- 初始化經(jīng)由靜態(tài)分配,且不使用默認(rèn)屬性的互斥量。
加鎖和解鎖互斥量
初始化之后,互斥量處于未鎖定狀態(tài)。函數(shù) pthread_mutex_lock()可以鎖定某一互斥量,而函數(shù) pthread_mutex_unlock()則可以將一個(gè)互斥量解鎖。
函數(shù)原型
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
1.創(chuàng)建互斥鎖
pthread_mutex_t mtx;
互斥鎖的類型是 pthread_mutex_t ,所以定義一個(gè)變量就是創(chuàng)建了一個(gè)互斥鎖:
2.初始化互斥鎖
//第二個(gè)參數(shù)為 NULL,互斥鎖的屬性會設(shè)置為默認(rèn)屬性 pthread_mutex_init(&mtx, NULL);
3.獲取互斥鎖
在進(jìn)行互斥操作的時(shí)候, 應(yīng)該先"拿到鎖"再執(zhí)行需要互斥的操作,否則可能會導(dǎo)致多個(gè)線程都需要訪問的數(shù)據(jù)結(jié)果不一致。
4.阻塞調(diào)用
pthread_mutex_lock(&mtx);
5.非阻塞調(diào)用
如果鎖被占用就不用,如果沒被占用那就用, 可以使用 pthread_mutex_trylock() 函數(shù)。 用法和pthread_mutex_lock() 用法類似,不過當(dāng)請求的鎖正在被占用的時(shí)候, 不會進(jìn)入阻塞狀態(tài),而是立刻返回,并返回一個(gè)錯(cuò)誤代碼 EBUSY,意思是說, 有其它線程正在使用這個(gè)鎖。
int err = pthread_mutex_trylock(&mtx);
if(0 != err) {
if(EBUSY == err) {
//The mutex could not be acquired because it was already locked.
}
}
6.超時(shí)調(diào)用
如果不想不斷的調(diào)用 pthread_mutex_trylock() 來測試互斥鎖是否可用, 而是想阻塞調(diào)用,但是增加一個(gè)超時(shí)時(shí)間, 用pthread_mutex_timedlock() 解決, 其調(diào)用方式如下:
struct timespec abs_timeout;
abs_timeout.tv_sec = time(NULL) + 1;
abs_timeout.tv_nsec = 0;
int err = pthread_mutex_timedlock(&mtx, &abs_timeout);
if(0 != err) {
if(ETIMEDOUT == err) {
//The mutex could not be locked before the specified timeout expired.
}
}
阻塞等待,但是只等待一秒鐘,后如果還沒拿到鎖的話, 那就返回,并返回一個(gè)錯(cuò)誤代碼 ETIMEDOUT,意思是超時(shí)了。
其中 timespec 定義在頭文件 time.h 中,其定義如下
struct timespec
{
__time_t tv_sec; /* Seconds. */
long int tv_nsec; /* Nanoseconds. */
};
這個(gè)函數(shù)里面的時(shí)間,是絕對時(shí)間,所以這里用 time() 函數(shù)返回的時(shí)間增加了 1 秒
7.釋放互斥鎖
用完互斥鎖,一定要記得釋放,下一個(gè)想要獲得這個(gè)鎖的線程, 只能去等。
釋放互斥鎖比較簡單,使用 pthread_mutex_unlock() 即可:
pthread_mutex_unlock(&mtx);
8.銷毀線程鎖
pthread_mutex_destroy(&mtx)
一個(gè)被銷毀的線程鎖可以被 pthread_mutex_init() 再次初始化。對被銷毀的線程鎖進(jìn)行其它操作,其結(jié)果是未定義的。
對一個(gè)處于已初始化但未鎖定狀態(tài)的線程鎖進(jìn)行銷毀是安全的。盡量避免對一個(gè)處于鎖定狀態(tài)的線程鎖進(jìn)行銷毀操作。
互斥量的死鎖
當(dāng)超過一個(gè)線程加鎖同一組互斥量時(shí),就有可能發(fā)生死鎖。
例,每個(gè)線程都成功地鎖住一個(gè)互斥量,接著試圖對已為另一線程鎖定的互斥量加鎖。

兩個(gè)線程將無限期等待
有兩種解決方法
1.當(dāng)多個(gè)線程對一組互斥量操作時(shí),總是應(yīng)該以相同順序?qū)υ摻M互斥量進(jìn)行鎖定,如果兩個(gè)線程總是先鎖定 mutex1 再鎖定 mutex2,死鎖就不會出現(xiàn)
2.使用頻率較低,就是“嘗試一下,然后恢復(fù)”,在這種方案中,線程先使用函數(shù)pthread_mutex_lock()鎖定第 1 個(gè)互斥量,然后使用函數(shù)pthread_mutex_trylock()來鎖定其余互斥量。如果任一pthread_mutex_trylock()調(diào)用失?。ǚ祷?EBUSY),那么該線程將釋放所有 互斥量,也許經(jīng)過一段時(shí)間間隔,從頭再試
注:
- 對共享資源操作前一定要獲得鎖。
- 完成操作以后一定要釋放鎖。
- 盡量短時(shí)間地占用鎖。
- 如果有多鎖, 如獲得順序是ABC連環(huán)扣, 釋放順序也應(yīng)該是ABC。
- 線程錯(cuò)誤返回時(shí)應(yīng)該釋放它所獲得的鎖。
例子
保護(hù)fp指向文件中數(shù)的累加正常進(jìn)行
static pthread_mutex_t mut=PTHREAD_MUTEX_INITIALIZER;
void *thr_prime(void *p)
{
FILE *fp;
char linebuf[linesize];
fp =fopen(fname,"r+"); //多個(gè)線程之間相撞拿到 同一個(gè)fp 開始覆蓋寫操作
if(fp == NULL)
{
perror("fopen");
exit(-1);
}
//加鎖
pthread_mutex_lock(&mut);
fgets(linebuf,linesize,fp);
fseek(fp,0,SEEK_SET);
fprintf(fp,"%d\n",atoi(linebuf)+1);
//解鎖
pthread_mutex_unlock(&mut);
fclose(fp);
pthread_exit(NULL);
}
int main()
{
int err,i;
pthread_t tid[thrnum];
//main 線程 進(jìn)行創(chuàng)建線程
for(i=0 ; i<=thrnum ;i++)
{
err =pthread_create(tid + i ,NULL,thr_prime,NULL);
if(err)
{
fprintf(stderr,"pthread_creat():%s\n",strerror(err));
exit(1);
}
}
//為線程收尸
for(i =0 ; i<=thrnum ;i++)
{
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&mut);
exit(0);
}到此這篇關(guān)于C++中關(guān)于互斥量的全面認(rèn)知的文章就介紹到這了,更多相關(guān)C++互斥量內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++?opencv利用grabCut算法實(shí)現(xiàn)摳圖示例
這篇文章主要為大家介紹了C++?opencv利用grabCut算法實(shí)現(xiàn)摳圖的代碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
C++的sstream標(biāo)準(zhǔn)庫詳細(xì)介紹
以下是對C++中的的sstream標(biāo)準(zhǔn)庫進(jìn)行了詳細(xì)的介紹,需要的朋友可以過來參考下2013-09-09
C++中4種強(qiáng)制類型轉(zhuǎn)換的區(qū)別總結(jié)
C++風(fēng)格的類型轉(zhuǎn)換提供了4種類型轉(zhuǎn)換操作符來應(yīng)對不同場合的應(yīng)用。下面這篇文章主要給大家介紹了C++中4種強(qiáng)制類型轉(zhuǎn)換的區(qū)別,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-12-12
OpenCV利用高斯模糊實(shí)現(xiàn)簡單的磨皮美顏效果
這篇文章主要介紹了通過OpenCV中的高斯模糊以及雙邊模糊來實(shí)現(xiàn)一個(gè)簡單的磨皮美顏效果,文中的講解很詳細(xì),感興趣的同學(xué)可以學(xué)習(xí)一下2021-12-12

