操作系統 — 淺析條件變量

淺析條件變量








Single UNIX Specification目前定義了條件變量的兩個屬性:進程共享屬性和時鐘屬性. 條件變量的作用用於多線程之間關於共享數據

狀態變化的信. 當一個動作需要另外一個動作完成時才能進行時. 也就是說:當一個線程的行爲依賴於另外一個線程對共享數據狀態

的改變時,這時候就可使用條件變量.假如沒有條件變量,舉個最常見的消費者生產者模型.小明去銀行去取錢,當他進入取款機的

小房子後,發現這個取款機中的錢不夠他的要的錢,然它離開了小房子,然後小明這個人性子比較急出去不到一秒,馬上又扭頭進去

ATM機的小房子那裏重新查詢資源夠不夠它拿,然後就可以看到一個人跟個傻子一樣在ATM機那裏出來進去,這個時候出現問題了!

如果這個銀行還有別的人取錢,然後機子還不夠,那麼小明後面會排好多人等它取完錢,但是小明就是要跟ATM機較勁每隔一秒就查看

一次. 這個時候他就浪費了資源,排在他身後的人可能有人取的錢ATM機裏面就夠用了,但是它沒有機會去取錢. 因爲小明一直在那裏

以極短的時間出來進去,別人根本就沒有機會.


如果你覺得小明太智障了,生活中怎麼可能會有這種人!! 但是我們把這個場景模仿到操作系統中,小明是一個優先級極高的線程

,然後它訪問一段資源,在他進入的時候將該資源加鎖,然後發現裏面的資源不夠自己使用然後再將資源解鎖讓別的線程使用. 但

是小明的優先級實在太高了,然後它又重複上面的操作,這種行爲對於CPU來說是非常浪費時間效率的. 當然小明可以這樣,發現資

源不夠使用的時候,sleep一段時間,然後再查詢,那麼問題來了! 睡多久! 時間太長,實時性不好. 時間太短,浪費CPU時間效率.


現在是擁有條件變量的情況! 小明訪問資源加上互斥量,然後發現資源不夠自己訪問,釋放互斥量,然後去旁邊等,等待工作人員

告訴他你可以來取錢了,ATM機裏面的錢夠你取了.  所以我們現在來認識條件變量吧! 既然它這麼強大. 在使用條件變量之前,必

須先對它進行初始化. 由pthread_cond_t數據類型表示的條件變量可以用兩種方式進行初始化,可以把常量

PTHREAD_COND_INITIALIZER賦給靜態分配的條件變量,但如果條件變量是動態分配的,則需要使用pthread_cond_init函數對它進行

初始化!在釋放條件變量底層的內存空間之前,可以使用pthread_cond_destroy函數對條件變量進行反初始化!!!!!

 #include <pthread.h>

int pthread_cond_destroy(pthread_cond_t*cond); 

int pthread_cond_init(pthread_cond_t *restrict cond,  const pthread_condattr_t *restrict attr);       

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
除非需要創建一個具有非默認屬性的條件變量,否則pthread_cond_init函數的attr參數可以設置爲NULL. 接下來我們瞭解那個最核心的

功能,被別人喚醒等待方式!我們使用pthread_cond_wait等待條件變量變爲真. 如果在給定的時間內條件不能滿足,那麼就會生成一個

返回錯誤碼變量.

 #include <pthread.h>

int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
兩個函數的返回值: 若成功返回0 否則 返回錯誤編號. 與前文無關,這是一個筆記(獲取當前時間  gettimeofday(&now,NULL));

傳遞給pthread_cond_wait的互斥量對條件進行保護. 調用者把鎖住的互斥量傳給函數,函數然後自動把調用線程放到等待條件的線程

列表上,對互斥量解鎖. 這就關閉了條件檢查和線程進入休眠狀態等待條件改變着兩個操作之間的時間通道,這樣線程就不會錯過

條件的任何變化. pthread_cond_wait返回之後,互斥量再次鎖住. pthread_cond_timedwait函數的功能與pthread_cond_wait函數相

似,只是多了一個超時值. 超時值指定了我們願意等待多長時間,他是通過tiomespec結構指定的. 需要指定原意等待多長時間,這個

時間值是一個絕對數而不是一個相對數.

例如我們想等3分鐘,那麼就是將當前時間加上3分鐘再轉換成timespec結構. 可以使用clock_gettime函數獲取timespec結構表示的

當前時間.如果超時到期時條件還是沒有出現,pthread_cond_timewait將重新獲取互斥量,然後返回錯誤ETIEDOUT. 從

pthread_cond_wait或者pthread_cond_timedwait調用成功返回時,線程需要重新計算條件,因爲另一個線程可能已經在運行並改變

了條件.

有兩個函數可以用於通知線程條件已經滿足. pthread_cond_signal函數至少能喚醒一個等待該條件的線程,而

pthread_cond_broadcast函數則能喚醒等待該條件的所有線程.

#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cond);

int pthread_cond_signal(pthread_cond_t *cond);
在調用pthread_cond_signal或者pthread_cond_broadcast時,我們說這是在給線程或者條件發信號. 一定在改變條件狀態之後再給線

程發信號.那我們來使用一下條件變量吧! 加入我們再練投籃一個人給你撿球,然後你接球投籃,那麼當別人給你傳球給你之前! 你

不能夠一直投籃吧?

(如果你練假動作除外),要不然會銷燬你的體力,那麼我們就寫一個投籃傳球使用條件變量的代碼吧:

/*************************************************************************
	> File Name: pthread4.c
	> Author: ma6174
	> Mail: [email protected] 
	> Created Time: Mon 22 Jan 2018 04:34:26 AM PST
 ************************************************************************/

#include<stdio.h>
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

pthread_cond_t g_cond;

pthread_mutex_t g_lock;
  
void* ThreadEntry1(void* arg)
{
	while(1)
	{
		printf("傳球->");
		pthread_cond_signal(&g_cond);
		usleep(678123);
	}
	return NULL;
}

void* ThreadEntry2(void* arg)
{
	while(1)
	{
		pthread_cond_wait(&g_cond,&g_lock);
		printf("投籃O\n");
		usleep(123456);
	}
	return NULL;
}

int main()
{
	pthread_cond_init(&g_cond,NULL);
	pthread_mutex_init(&g_lock,NULL);

	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,ThreadEntry1,NULL);
	pthread_create(&tid2,NULL,ThreadEntry2,NULL);
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	pthread_cond_destroy(&g_cond);
	pthread_mutex_destroy(&g_lock);
}

所以運行結果應該就是傳球 投球. 這樣就不會浪費資源了! 那麼我們來看執行完畢的結果是什麼? ? ? ? ?



這結果就是我們所預料的,看完這個博客我相信你對條件變量會有自己的一定的理解! 我們一定要熟悉條件變量的相關函數參數以及使用場景.

特別是pthread_cond_wait. 以及瞭解條件變量的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章