目錄
一 前言
二 線程饑餓
三 線程同步
四 條件變量
1. cond (?condition)
2. pthread_cond_wait()?:
3. pthread_cond_signal()
?五 條件變量的使用
一 前言
?在上篇文章Linux : 多線程互斥-CSDN博客我們講解了線程互斥的概念,為了防止多個線程同時訪問一份臨界資源而出問題,我們引入了線程互斥,線程互斥其實就是多個線程同時爭搶一份資源,誰搶到了就是誰的,搶不到的只能等待著下一次搶。雖然解決了多個線程同時訪問同一資源所產生的問題,但是我們思考一下這樣子合理嗎?不合理,這會產生另一種問題——線程饑餓。
二 線程饑餓
?那么線程饑餓是什么呢?為了便于理解,我們可以極端的考慮問題,假設在多線程情況下,存在著兩類優先級不同的線程,一類線程的優先級非常高,另一類的線程的優先級非常低,他們開始同時爭搶臨界資源,假設高優先級的線程拿到了資源,上了鎖之后,其他的線程只能等。直到該線程使用完臨近資源后解鎖,接著所有線程又開始爭搶資源,而高優先級的線程因為其優先性會再一次爭搶到資源,如循環往復,導那些低優先級的線程總是在等待中,永遠拿不到或者很少次數拿到資源,這樣被稱為饑餓或者餓死。這種爭搶臨界資源的方式雖然是沒有什么錯誤,但是總歸來說是不合理的。
三 線程同步
在線程只使用互斥的方式去訪問臨界資源的時候,就可能會出現某些線程饑餓的情況。那么在操作系統中有沒有一種機制,在某一時刻既可以只讓一個線程去訪問臨界資源,但是又可以讓所有的的線程按照一定的順序訪問資源呢?所有的線程就像排隊一樣一個個輪流訪問資源,當某一線程訪問玩臨界資源的時候,他就去隊尾等待。這樣所有的線程的執行流都可以訪問到資源,從而杜絕了線程饑餓的問題。 ?這樣的機制叫做——同步,即線程同步:在保證臨界資源安全的前提下,讓執行流訪問臨界資源具有一定的順序性。
四 條件變量
?那么同步是怎么實現的呢?同步離不開一個東西——條件變量,條件變量是一種可以實現線程同步的機制,通過條件變量,可以實現讓線程有序的訪問臨界資源。
條件變量,顧名思義它是一個執行的“條件”,當線程需要訪問臨界資源時,如果臨界資源不滿足一定的條件,那就讓線程進行等待,如果滿足條件,則讓線程繼續恢復執行的機制。它是?一個?pthread_cond_t?結構體類型的變量,并且在 pthread 庫中也提供了一些條件變量相關的接口。
1. cond (?condition)
pthread_cond_t?是定義條件變量的類型。
條件變量的使用是和互斥鎖差不多的。?
-
條件變量的初始化可以和互斥量相同有兩種,一種是調用接口 pthread_cond_init() 初始化,第一個參數是條件變量的地址,第二個參數是條件變量的屬性(暫時不考慮)。需要注意的是,用該接口初始化的條件變量在不需要使用的時候,需要調用 pthread_cond_destroy() 接口來銷毀掉。
- 使用宏初始化的條件變量就不用手動調用接口來銷毀了。
2. pthread_cond_wait()?:
條件變量等待的接口
-
這么多等待的接口中 pthread_cond_wait() 接口是最常用的,它是pthread庫提供的使用條件變量等待的接口,線程調用此接口,線程就會立即進入等待。
-
pthread_cond_timedwait() 也是pthread提供給的使用條件變量等待的接口,不過看他的名字也知道它是一種定時讓線程等待的接口,即可以通過該接口設置一定的時間,在此時間內讓線程等待,如果此時間內,條件滿足了,線程就會被自動喚醒,繼續執行代碼。
-
我們可以看到這兩個接口的參數中都有 互斥鎖 ,他們是和互斥鎖一起配合使用的。
上面講到了兩個通過條件變量讓線程進行等待的接口,既然有等待的接口,那么自然就存在著通過條件變量去喚醒線程的接口。如下
3. pthread_cond_signal()
調用該接口可以讓某個通過指定條件變量陷入等待的線程被喚醒。
?五 條件變量的使用
下面我們寫個測試使用一下條件變量
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>int tickets=1000;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//定義一個鎖
pthread_cond_t cond =PTHREAD_COND_INITIALIZER;//定義一個條件變量void* start_routine(void* args)
{std::string name =static_cast<const char*>(args);while(true){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);std::cout<<name<<" ->"<<tickets--<<std::endl;pthread_mutex_unlock(&mutex);}
}int main()
{//通過條件變量控制線程的執行pthread_t t1,t2;pthread_create(&t1,nullptr,start_routine,(void*)"thread 1");pthread_create(&t2,nullptr,start_routine,(void*)"thread 2");while(true){sleep(1);pthread_cond_signal(&cond);//喚醒該條件下所以的線程std::cout<<"main thread wake up one thread...."<<std::endl;}pthread_join(t1,nullptr);pthread_join(t2,nullptr);return 0;
}
測試結果?
從測試結果可以看到pthread_cond_signal()對線程的喚醒是以一定順序來進行的。注意,pthread_cond_signal()是一次對一個線程進行喚醒,我們也可以使用? ? ? ? ? ? ? ? ? ? ? pthread_cond_broadcast()來喚醒所有的在等待中的線程。?
int main()
{//通過條件變量控制線程的執行pthread_t t1,t2;pthread_create(&t1,nullptr,start_routine,(void*)"thread 1");pthread_create(&t2,nullptr,start_routine,(void*)"thread 2");while(true){sleep(1);//一次喚醒所以線程pthread_cond_broadcast(&cond);//喚醒該條件下所以的線程std::cout<<"main thread wake up one thread...."<<std::endl;}pthread_join(t1,nullptr);pthread_join(t2,nullptr);return 0;
}
?條件變量:通過條件控制線程的執行