條件變數是用來等待執行緒而不是上鎖的,條件變數通常和互斥鎖一起使用,條件變數之所以要和互斥鎖一起使用,主要是因為互斥鎖的一個明顯的特點就是它只有兩種狀態:鎖定和非鎖定,而條件變數可以通過允許執行緒阻塞和等待另一個執行緒發送信號來彌補互斥鎖的不足,所以互斥鎖和條件變數通常一起使用,
當條件滿足的時候,執行緒通常解鎖并等待該條件發生變化,一旦另一個執行緒修改了環境變數,就會通知相應的環境變數喚醒一個或者多個被這個條件變數阻塞的執行緒,這些被喚醒的執行緒將重新上鎖,并測驗條件是否滿足,一般來說條件變數被用于執行緒間的同步;當條件不滿足的時候,允許其中的一個執行流掛起和等待,
簡而言之,條件變數本身不是鎖,但它也可以造成執行緒阻塞,通常與互斥鎖配合使用,給多執行緒提供一個會合的場所,
條件變數的優點:
相較于mutex而言,條件變數可以減少競爭,如果僅僅是mutex,那么,不管共享資源里有沒資料,生產者及所有消費都全一窩蜂的去搶鎖,會造成資源的浪費,
如直接使用mutex,除了生產者、消費者之間要競爭互斥量以外,消費者之間也需要競爭互斥量,但如果匯聚(鏈表)中沒有資料,消費者之間競爭互斥鎖是無意義的,有了條件變數機制以后,只有生產者完成生產,才會引起消費者之間的競爭,提高了程式效率,
主要應用函式:
pthread_cond_init函式
pthread_cond_destroy函式
pthread_cond_wait函式
pthread_cond_timedwait函式
pthread_cond_signal函式
pthread_cond_broadcast函式
以上6 個函式的回傳值都是:成功回傳0, 失敗直接回傳錯誤號,
pthread_cond_t型別:用于定義條件變數,比如:pthread_cond_t cond;
pthread_cond_init函式
函式原型:
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
函式作用:
初始化一個條件變數
引數說明:
cond:條件變數,呼叫時應傳&cond給該函式
attr:條件變數屬性,通常傳NULL,表示使用默認屬性
也可以使用靜態初始化的方法,初始化條件變數:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_cond_destroy函式
函式原型:
int pthread_cond_destroy(pthread_cond_t *cond);
函式作用:
銷毀一個條件變數
pthread_cond_wait函式
函式原型:
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
函式作用:
阻塞等待一個條件變數,具體而言有以下三個作用:
- 阻塞等待條件變數cond(參1)滿足;
- 釋放已掌握的互斥鎖mutex(解鎖互斥量)相當于pthread_mutex_unlock(&mutex);
- 當被喚醒,pthread_cond_wait函式回傳時,解除阻塞并重新申請獲取互斥鎖
其中1、2.兩步為一個原子操作,
pthread_cond_timedwait函式
函式原型:
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
函式作用:
限時等待一個條件變數
引數說明:
前兩個比較好理解,重點說明第三個引數,
這里有個struct timespec結構體,可以在man sem_timedwait中查看,結構體原型如下:
struct timespec {
? time_t tv_sec; /* seconds */ 秒
? long tv_nsec; /* nanosecondes*/ 納秒
}
struct timespec定義的形參abstime是個絕對時間,注意,是絕對時間,不是相對時間,什么是絕對時間?2018年10月1日10:10:00,這就是一個絕對時間,什么是相對時間?給洗衣機定時30分鐘洗衣服,就是一個相對時間,也就是說從當時時間開始計算30分鐘,諸如此類,
如:time(NULL)回傳的就是絕對時間,而alarm(1)是相對時間,相對當前時間定時1秒鐘,
adstime所相對的時間是相對于1970年1月1日00:00:00,也就是UNIX計時元年,
下面給出一個錯誤用法:
struct timespec t = {1, 0};
pthread_cond_timedwait (&cond, &mutex, &t);
這種用法只能定時到 1970年1月1日 00:00:01秒,想必這個時間大家都還沒出生,
正確用法:
time_t cur = time(NULL); 獲取當前時間,
struct timespec t; 定義timespec 結構體變數t
t.tv_sec = cur+1; 定時1秒
pthread_cond_timedwait (&cond, &mutex, &t); 傳參
pthread_cond_signal函式
函式原型:
int pthread_cond_signal(pthread_cond_t *cond);
函式作用:
喚醒至少一個阻塞在條件變數上的執行緒
pthread_cond_broadcast函式
函式原型:
int pthread_cond_broadcast(pthread_cond_t *cond);
函式作用:
喚醒全部阻塞在條件變數上的執行緒
生產者消費者條件變數模型
不管是什么語言,只要提到執行緒同步,一個典型的案例就是生產者消費者模型,在Linux環境下,借助條件變數來實作這一模型,是比較常見的一種方法,
假定有兩個執行緒,一個模擬生產者行為,一個模擬消費者行為,兩個執行緒同時操作一個共享資源(一般稱之為匯聚),生產向其中添加產品,消費者從中消費掉產品,
看如下示例,使用條件變數模擬生產者、消費者問題:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
typedef struct msg {
struct msg *next;
int num;
}msg_t;
msg_t *head = NULL;
msg_t *mp = NULL;
/* 靜態初始化 一個條件變數 和 一個互斥量*/
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;
void *th_producer(void *arg)
{
while (1) {
mp = malloc(sizeof(msg_t));
mp->num = rand() % 1000; //模擬生產一個產品
printf("--- produce: %d --------\n", mp->num);
pthread_mutex_lock(&mutex);
mp->next = head;
head = mp;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&has_product); //喚醒執行緒去消費產品
sleep(rand() % 5);
}
return NULL;
}
void *th_consumer(void *arg)
{
while (1) {
pthread_mutex_lock(&mutex);
while (head == NULL) { //如果鏈表里沒有產品,就沒有搶鎖的必要,一直阻塞等待
pthread_cond_wait(&has_product, &mutex);
}
mp = head;
head = mp->next; //模擬消費掉一個產品
pthread_mutex_unlock(&mutex);
printf("========= consume: %d ======\n", mp->num);
free(mp);
mp = NULL;
sleep(rand() % 5);
}
return NULL;
}
int main()
{
pthread_t pid, cid;
srand(time(NULL));
pthread_create(&pid, NULL, th_producer, NULL);
pthread_create(&cid, NULL, th_consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
運行結果:

更多精彩內容,請關注公眾號良許Linux,公眾內回復1024可免費獲得5T技術資料,包括:Linux,C/C++,Python,樹莓派,嵌入式,Java,人工智能,等等,公眾號內回復進群,邀請您進高手如云技術交流群,
公眾號:良許Linux
有識訓?希望老鐵們來個三連擊,給更多的人看到這篇文章
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/128314.html
標籤:其他
上一篇:作業系統-schedule函式
下一篇:有個疑問,關于login命令
