相交進程之間的關系主要有兩種,同步與互斥。
所謂互斥,是指散布在不同進程之間的若干程序片斷,當某個進程運行其中一個程序片段時,其它進程就不能運行它們之中的任一程序片段,只能等到該進程運行完這個程序片段后才可以運行。
所謂同步,是指散布在不同進程之間的若干程序片斷,它們的運行必須嚴格按照規定的某種先后次序來運行,這種先后次序依賴于要完成的特定的任務。
顯然,同步是一種更為復雜的互斥,而互斥是一種特殊的同步。
也就是說互斥是兩個線程之間不可以同時運行,他們會相互排斥,必須等待一個線程運行完畢,另一個才能運行,而同步也是不能同時運行,但他是必須要安照某種次序來運行相應的線程(也是一種互斥)!
總結:互斥:是指某一資源同時只允許一個訪問者對其進行訪問,具有唯一性和排它性。但互斥無法限制訪問者對資源的訪問順序,即訪問是無序的。
同步:是指在互斥的基礎上(大多數情況),通過其它機制實現訪問者對資源的有序訪問。在大多數情況下,同步已經實現了互斥,特別是所有寫入資源的情況必定是互斥的。少數情況是指可以允許多個訪問者同時訪問資源。
- 讀寫鎖特點:??
- 1)多個讀者可以同時進行讀??
- 2)寫者必須互斥(只允許一個寫者寫,也不能讀者寫者同時進行)??
- 3)寫者優先于讀者(一旦有寫者,則后續讀者必須等待,喚醒時優先考慮寫者)??
- ???
- 互斥鎖特點:??
- ??一次只能一個線程擁有互斥鎖,其他線程只有等待??
- ??
- 互斥鎖??
- pthread_mutex_init()??
- pthread_mutex_lock()??
- pthread_mutex_unlock()??
-
- 讀寫鎖 ?
- pthread_rwlock_init()??
- pthread_rwlock_rdlock()??
- pthread_rwlock_wrlock()??
- pthread_rwlock_unlock()??
-
- 條件變量 ?
- pthread_cond_init()??
- pthread_cond_wait()??
- pthread_cond_signal()??
條件變量(Condtion Variable)是在多線程程序中用來實現“等待->喚醒”邏輯常用的方法。
舉個簡單的例子,應用程序A中包含兩個線程t1和t2。t1需要在bool變量test_cond為true時才能繼續執行,而test_cond的值是由t2來改變的,這種情況下,如何來寫程序呢?可供選擇的方案有兩種:
- 第一種是t1定時的去輪詢變量test_cond,如果test_cond為false,則繼續休眠;如果test_cond為true,則開始執行。
- 第二種就是上面提到的條件變量,t1在test_cond為false時調用cond_wait進行等待,t2在改變test_cond的值后,調用cond_signal,喚醒在等待中的t1,告訴t1 test_cond的值變了,這樣t1便可繼續往下執行。
??????很明顯,上面兩種方案中,第二種方案是比較優的。在第一種方案中,在每次輪詢時,如果t1休眠的時間比較短,會導致cpu浪費很厲害;如果t1休眠的時間比較長,又會導致應用邏輯處理不夠及時,致使應用程序性能下降。第二種方案就是為了解決輪詢的弊端而生的。然而條件變量在使用的過程中,比較容易出錯,如何用得不正確的話,會適得其反的,接下來,我將詳細分析如何來使用條件變量,希望能夠給在使用條件變量過程中遇到問題的朋友有所幫助。
??????在開始介紹之前,需要說明一下,在接下來的介紹中,需要用到互斥鎖和條件變量相關的內容,在這里我以Linux下的pthread_mutex_t為互斥鎖類型,pthread_cond_t為條件變量類型來進行介紹,對pthread不熟的朋友,可以參考一下linux下的manual。
??????1. 下面是把剛開始舉的例子翻譯后的程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | pthread_mutex_t mutex; ///< 互斥鎖
pthread_cond_t cond; ///< 條件變量
bool test_cond = false;
/// TODO 初始化mutex和cond/// thread 1:
pthread_mutex_lock(&mutex); ///< 1
while (!test_cond)
{pthread_cond_wait(&cond, &mutex); ///< 2,3
}
pthread_mutex_unlock(&mutex); ///< 4
RunThread1Func();/// thread 2:
pthread_mutex_lock(&mutex); ///< 5
test_cond = true;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex); ///< 6/// TODO 銷毀mutex和cond
|
??????通過上面的例子,下面我來介紹一下條件變量在使用過程中需要注意的幾點(也是比較容易出錯的):
??????(1)條件變量的使用過程中,最為關鍵的一點是互斥鎖的使用。細心的朋友應該發現了,我在上面的例子中標了1、2、3、4、5、6個標號。在這里1、4、5、6都是正常的lock/unlock,2、3是需要特別說明的。2是進入pthread_cond_wait后的,pthread_cond_wait調的pthread_mutex_unlock,這樣做的目的是為了保證在thread1阻塞wait后,thread2獲取同一把鎖mutex的時候,能夠正常獲取(即5,6)。3是thread1被喚醒后,要退出pthead_cond_wait之前,pthread_cond_wait調的pthread_mutex_lock,這樣做的目的是為了把mutex的控制權還給調用pthread_cond_wait的線程(即thread1)。整理一下基本的時序為:
1
2
3 | thread 1 lock->thread 1 wait-> thread 1 unlock(in wait)
->thread 2 lock->thread 2 signal->thread 2 unlock
->thread 1 lock(in wait)->thread 1 unlock
|
??????(2)條件變量使用的過程中,通常會加一個bool或者int的值test_cond來配合使用。這里需要注意的一點是一定要在signal之前來改變test_cond,這樣才能保證wait的線程被喚醒后,能夠取到正確的test_cond的值,否則后果是不可預測的。