文章目錄
- 前言
- 線程同步的基本概念
- 條件變量
- 定義條件變量
- 初始化條件變量
- 銷毀條件變量
- 等待條件(重要)
- 喚醒等待
- 簡單運用
- 常見使用條件變量的格式
前言
線程同步意味著在多線程并發執行中,協調線程之間的執行順序,以確保共享資源被正確訪問和修改。線程同步的維護本質就是在安排線程之間的執行順序。那么在linux中是如何維護線程同步的呢?本篇文章將圍繞這個為題展開敘述。
線程同步的基本概念
下面介紹一些有關線程同步的基本概念。
條件變量
當一個線程互斥的訪問某個變量,即訪問臨界資源時給臨界區上互斥鎖,這個時候其它線程只能等待。那什么時候其它線程可以繼續申請臨界資源呢?我們希望當一個線程使用完臨界資源后,正在等待的線程能夠知道這一事件的發生從而重新申請資源,而不是一直重復申請這個動作。
就像一個鬧鐘,當鬧鐘響了之后我們就知道該起床了,而不是睡一下又看下時間。
條件變量提供一種線程通信的方法,使得一個線程可以等待另一個線程滿足某種條件后再繼續執行。具體的,我們將這種通知一個線程繼續執行的動作稱為喚醒。
于是,借助條件變量,我們就能實現協調線程之間訪問臨時資源的順序性。
同時線程庫給我們提供了一些接口用來操作條件變量,下面介紹一些常見的關于條件變量的操作。(頭文件都是pthread.h
)
定義條件變量
初始化條件變量
- 動態初始化
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
cond
:要初始化的條件變量attr
:條件變量的屬性,通常是NULL
- 靜態初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
銷毀條件變量
int pthread_cond_destroy(pthread_cond_t *cond);
銷毀某個條件變量,成功返回0,失敗返回錯誤代碼
等待條件(重要)
如果當前線程沒有申請到臨界資源,該線程可以選擇等待。
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
在調用該函數時,互斥鎖mutex
必須被鎖住,并等待喚醒。喚醒之后線程重新鎖住互斥鎖并繼續執行。值得注意的是,pthread_cond_wait
函數首先會解鎖與之關聯的互斥鎖mutex
,這也是為什么使用該函數時mutex
必須是被鎖住的。然后調用該函數的線程進入阻塞狀態,直到被喚醒。最后,條件變量被通知之后,pthread_cond_wait
重新鎖定互斥鎖mutex
。
對于該函數提出以下問題:
- 為什么要在
pthread_cond_wait
中傳入互斥鎖?- 在調用 pthread_cond_wait 時,互斥鎖是已經鎖住的,確保沒有其他線程可以修改共享資源。
- pthread_cond_wait 在進入等待狀態之前會自動釋放互斥鎖,使得其他線程可以修改條件。
- 當線程被喚醒后,pthread_cond_wait 會重新獲得互斥鎖,然后再繼續執行,因為此時還在臨界區,還會訪問臨界資源。
喚醒等待
- 喚醒某個線程
int pthread_cond_signal(pthread_cond_t *cond);
喚醒一個等待在條件變量 cond 上的線程。如果有多個線程在等待條件變量,具體喚醒哪一個線程是不確定的。
成功返回0,失敗則返回錯誤碼
- 喚醒所有正在等待該條件變量的線程
int pthread_cond_broadcast(pthread_cond_t *cond);
喚醒所有等待在條件變量 cond 上的線程。同樣成功返回0,失敗則返回錯誤碼。
簡單運用
下面我們使用條件變量和互斥鎖來設計一個簡單的代碼樣例
#include <iostream>
#include <unistd.h>
#include <pthread.h>using namespace std;pthread_cond_t cond;
pthread_mutex_t mutex;void *r1(void *arg) // 等待函數,執行該函數的線程一直處于while (true)
{pthread_cond_wait(&cond, &mutex);cout << "被喚醒" << endl;return arg;
}void *r2(void *arg) // 喚醒函數,執行該函數的線程一直嘗試喚醒某個等待的線程
{while (true){pthread_cond_signal(&cond);cout << "喚醒某個線程" << endl;sleep(1);}
}int main()
{pthread_t t1, t2; // 定義兩個線程pthread_cond_init(&cond, NULL); // 初始化條件變量pthread_mutex_init(&mutex, NULL); // 初始化互斥鎖pthread_create(&t1, NULL, r1, NULL); // 創建線程并分配執行函數pthread_create(&t1, NULL, r2, NULL);pthread_join(t1, NULL); // 等待線程退出pthread_join(t2, NULL);pthread_mutex_destroy(&mutex); // 銷毀互斥鎖和條件變量pthread_cond_destroy(&cond);return 0;
}
常見使用條件變量的格式
- 等待條件代碼:
pthread_mutex_lock(&mutex); while (條件為假) //不滿足條件陷入等待,在循環中等待是為了防止偽喚醒pthread_cond_wait(cond, mutex);
// ...
// 訪問臨界資源//...pthread_mutex_unlock(&mutex);
- 給條件發送信號,即可以喚醒等待條件中的線程
pthread_mutex_lock(&mutex); 設置條件為真 pthread_cond_signal(cond); //喚醒某個線程pthread_mutex_unlock(&mutex);