條件變量相關(guān)函數(shù)
初始化條件變量-pthread_cond_init
初始化條件變量的函數(shù)叫做pthread_cond_init
#include<pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
參數(shù)說明
cond:需要初始化的條件變量
attr:初始化條件變量的屬性,一般設(shè)置為NULL
返回值說明
初始化成功返回0,失敗返回錯(cuò)誤碼
注意:調(diào)用pthread_cond_init
函數(shù)初始化條件變量叫做動(dòng)態(tài)分配,我們還可以用靜態(tài)分配的方式初始化條件變量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
銷毀條件變量-pthread_cond_destroy
銷毀條件變量的函數(shù)叫做pthread_cond_destroy
#include<pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
參數(shù)說明
cond:需要銷毀的條件變量
返回值說明
銷毀成功返回0,失敗返回錯(cuò)誤碼
注意:使用PTHREAD_COND_INITIALIZER
初始化 (靜態(tài)分配)的條件變量不需要銷毀
等待條件變量-pthread_cond_wait
等待條件變量滿足的函數(shù)叫做pthread_cond_wait
#include<pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
參數(shù)說明
cond:需要等待的條件變量
mutex:當(dāng)前線程所處臨界區(qū)對(duì)應(yīng)的互斥鎖
返回值說明
函數(shù)調(diào)用成功返回0,失敗返回錯(cuò)誤碼
喚醒等待條件變量
喚醒等待的函數(shù)有以下兩個(gè)
pthread_cond_broadcast
#include<pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
pthread_cond_signal
#include<pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
參數(shù)說明
cond:?jiǎn)拘言赾ond條件變量下等待的線程
返回值說明
函數(shù)調(diào)用成功返回0,失敗返回錯(cuò)誤碼
區(qū)別
- pthread_cond_signal函數(shù)用于喚醒等待隊(duì)列中首個(gè)線程
- pthread_cond_broadcast函數(shù)用于喚醒等待隊(duì)列中的全部線程
小例子
下面我們用主線程創(chuàng)建三個(gè)新線程,讓主線程控制這三個(gè)新線程, 這三個(gè)新線程創(chuàng)建后都在條件變量下進(jìn)行等待,直到被主線程喚醒
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mtx;//互斥鎖
pthread_cond_t cond;//條件變量
//主線程控制新線程
void *ctrl(void *args)
{
std::string name = (char*)args;
while(1)
{
std::cout << "master say : begin work" << std::endl;
//pthread_cond_signal函數(shù)作用:喚醒在條件變量下等待的 一個(gè) 線程
//哪一個(gè)呢?當(dāng)前在等待隊(duì)列里等待的第一個(gè)線程
pthread_cond_signal(&cond);
sleep(2);
}
}
void *work(void *args)
{
int number = *(int*)args;
delete (int*)args;
while(1)
{
pthread_cond_wait(&cond, &mtx);//等待條件變量
std::cout << "worker: " << number << " is working ..." << std::endl;
}
}
#define NUM 3
int main()
{
pthread_mutex_init(&mtx, nullptr);//初始化這把鎖
pthread_cond_init(&cond, nullptr);//初始化條件變量
pthread_t master;
pthread_t worker[NUM];
pthread_create(&master, nullptr, ctrl, (void*)"boss");
//創(chuàng)建NUM個(gè)線程
for(int i = 0; i < NUM; i++)
{
int *number = new int(i);
pthread_create(worker+i, nullptr, work, (void*)number);
}
//線程等待
for(int i = 0; i < NUM; i++)
{
pthread_join(worker[i], nullptr);
}
pthread_join(master, nullptr);//線程等待
pthread_mutex_destroy(&mtx);//釋放鎖
pthread_cond_destroy(&cond);//釋放條件變量
return 0;
}
此時(shí)我們會(huì)發(fā)現(xiàn)喚醒這三個(gè)線程時(shí)具有明顯的順序性
根本原因是當(dāng)這若干個(gè)線程啟動(dòng)時(shí)默認(rèn)都會(huì)在該條件變量下去等待,而我們每次都喚醒的是在當(dāng)前條件變量下等待的頭部線程,當(dāng)該線程執(zhí)行完打印操作后會(huì)繼續(xù)排到等待隊(duì)列的尾部進(jìn)行wait,所以我們能夠看到一個(gè)周轉(zhuǎn)的現(xiàn)象
如果我們想每次喚醒都將在該條件變量下等待的所有線程進(jìn)行喚醒,可以將代碼中的pthread_cond_signal
函數(shù)改為pthread_cond_broadcast
函數(shù)
//主線程控制新線程
void *ctrl(void *args)
{
std::string name = (char*)args;
while(1)
{
std::cout << "master say : begin work" << std::endl;
pthread_cond_broadcast(&cond);
sleep(2);
}
}
此時(shí)我們每一次喚醒都會(huì)將所有在該條件變量下等待的線程進(jìn)行喚醒==>也就是每次都將這三個(gè)線程喚醒
關(guān)于等待函數(shù)的補(bǔ)充
為什么
pthread_cond_wait
函數(shù)的第二個(gè)參數(shù)需要傳入互斥量
1)條件等待是線程間同步的一種手段,如果只有一個(gè)線程,條件不滿足,一直等下去都不會(huì)滿足,所以必須要有一個(gè)線程通過某些操作,改變共享變量,使原先不滿足的條件變得滿足,并且友好的通知等待在條件變量上的線程
2)條件不會(huì)無緣無故的突然變得滿足了,必然會(huì)牽扯到共享數(shù)據(jù)的變化,所以一定要用互斥鎖來保護(hù),沒有互斥鎖就無法安全的獲取和修改共享數(shù)據(jù)
3)當(dāng)線程進(jìn)入臨界區(qū)時(shí)需要先加鎖,然后判斷內(nèi)部資源的情況,若不滿足當(dāng)前線程的執(zhí)行條件,則需要在該條件變量下進(jìn)行等待,但此時(shí)該線程是拿著鎖被掛起的,也就意味著這個(gè)鎖再也不會(huì)被釋放了,此時(shí)就會(huì)發(fā)生死鎖問題
4)所以在調(diào)用pthread_cond_wait
函數(shù)時(shí),還需要將對(duì)應(yīng)的互斥鎖傳入,此時(shí)當(dāng)線程因?yàn)槟承l件不滿足需要在該條件變量下進(jìn)行等待時(shí),就會(huì)自動(dòng)釋放該互斥鎖
5)當(dāng)該線程被喚醒時(shí),該線程會(huì)接著執(zhí)行臨界區(qū)內(nèi)的代碼,此時(shí)便要求該線程必須立馬獲得對(duì)應(yīng)的互斥鎖,因此當(dāng)某一個(gè)線程被喚醒時(shí),實(shí)際會(huì)自動(dòng)獲得對(duì)應(yīng)的互斥鎖
總結(jié):
- 當(dāng)該線程進(jìn)入等待的時(shí)候,互斥鎖會(huì)自動(dòng)釋放,而當(dāng)該線程被喚醒時(shí),又會(huì)自動(dòng)獲得對(duì)應(yīng)的互斥鎖
- 條件變量需要配合互斥鎖使用,其中條件變量是用來完成同步的,而互斥鎖是用來完成互斥的
-
pthread_cond_wait
函數(shù)有兩個(gè)功能,一就是讓線程在特定的條件變量下等待,二就是讓線程釋放對(duì)應(yīng)的互斥鎖
我們可以不可以:當(dāng)我們進(jìn)入臨界區(qū)上鎖后,如果發(fā)現(xiàn)條件不滿足,先解鎖,然后在該條件變量下進(jìn)行等待
即:
//錯(cuò)誤的設(shè)計(jì)
pthread_mutex_lock(&mutex);
while (condition_is_false){
pthread_mutex_unlock(&mutex);//解鎖
//解鎖之后,等待之前,條件可能已經(jīng)滿足,信號(hào)已經(jīng)發(fā)出,但是該信號(hào)可能被錯(cuò)過
pthread_cond_wait(&cond);
pthread_mutex_lock(&mutex);//再加鎖
}
pthread_mutex_unlock(&mutex);//解鎖
不可行!因?yàn)?strong>解鎖和等待不是原子操作,調(diào)用解鎖之后,在調(diào)用pthread_cond_wait
函數(shù)之前,如果已經(jīng)有其他線程獲取到互斥量,發(fā)現(xiàn)此時(shí)條件滿足,于是發(fā)送了信號(hào),那么此時(shí)pthread_cond_wait
函數(shù)將錯(cuò)過這個(gè)信號(hào),最終可能會(huì)導(dǎo)致線程永遠(yuǎn)不會(huì)被喚醒,因此解鎖和等待必須是一個(gè)原子操作
進(jìn)入pthread_cond_wait
函數(shù)后,會(huì)先判斷條件變量是否等于0,若等于0則說明不滿足,此時(shí)會(huì)先將對(duì)應(yīng)的互斥鎖解鎖,直到pthread_cond_wait
函數(shù)返回時(shí)再將條件變量改為1,并將對(duì)應(yīng)的互斥鎖加鎖
條件變量使用規(guī)范
等待條件變量的代碼文章來源:http://www.zghlxwxcb.cn/news/detail-464780.html
pthread_mutex_lock(&mutex);//加鎖
while (條件為假)
pthread_cond_wait(&cond, &mutex);//條件不滿足,就一直等待
修改條件
pthread_mutex_unlock(&mutex);//解鎖
喚醒等待線程的代碼文章來源地址http://www.zghlxwxcb.cn/news/detail-464780.html
pthread_mutex_lock(&mutex);//加鎖
設(shè)置條件為真
pthread_cond_signal(&cond);//滿足條件了,喚醒在條件變量等待隊(duì)列當(dāng)中的第一個(gè)線程
pthread_mutex_unlock(&mutex);//解鎖
到了這里,關(guān)于【Linux】線程同步的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!