前言
pthread(POSIX thread),簡(jiǎn)稱為pthread,是線程的POSIX標(biāo)準(zhǔn),在類Unix操作系統(tǒng)中(Unix、Linux、Mac OS X等),都是用pthread作為操作系統(tǒng)的線程。<pthread.h>作為其編程標(biāo)準(zhǔn)的頭文件,本文探討里面的常用函數(shù)意義以及使用方法。
在多線程編程中,操作系統(tǒng)引入了鎖機(jī)制。通過(guò)鎖機(jī)制,能夠保證在多核多線程環(huán)境中,在某一個(gè)時(shí)間點(diǎn)上,只能有一個(gè)線程進(jìn)入臨界區(qū)代碼,從而保證臨界區(qū)中操作數(shù)據(jù)的一致性。
在pthread里面實(shí)現(xiàn)了5中同步變量,分別是互斥鎖(mutex)、自旋鎖(spinlock)、條件變量(condition)、讀寫(xiě)鎖(rwlock)以及柵欄(barrier)。
線程同步變量
pthread互斥鎖(mutex)
互斥鎖是一個(gè)二元變量,其狀態(tài)為開(kāi)鎖(允許0)和上鎖(禁止1),將某個(gè)共享資源與某個(gè)特定互斥鎖在邏輯上綁定(要申請(qǐng)?jiān)撡Y源必須先獲取鎖)。
訪問(wèn)公共資源前,必須申請(qǐng)?jiān)摶コ怄i,若處于開(kāi)鎖狀態(tài),則申請(qǐng)到鎖對(duì)象,并立即占有該鎖,以防止其他線程訪問(wèn)該資源;如果該互斥鎖處于鎖定狀態(tài),則阻塞當(dāng)前線程。
只有鎖定該互斥鎖的進(jìn)程才能釋放該互斥鎖,其他線程試圖釋放無(wú)效?;コ怄i使用的非常廣泛,在所有線程同步變量中,可以重點(diǎn)關(guān)注。
pthread_mutex_t
鎖類型,定義互斥鎖。
pthread_mutex_init
初始化互斥鎖。
pthread_mutex_lock
申請(qǐng)互斥鎖并占有互斥鎖,其他線程無(wú)法訪問(wèn)該資源。
pthread_mutex_trylock
pthread_mutex_unlock
釋放互斥鎖,此時(shí)其他線程可以訪問(wèn)該資源。
pthread_mutex_destroy
釋放鎖資源。
pthread條件變量(condition)
條件變量是利用線程間共享的全局變量進(jìn)行同步的一種機(jī)制,主要包括兩個(gè)動(dòng)作:一個(gè)線程等待"條件變量的條件成立"而掛起;另一個(gè)線程使"條件成立"(給出條件成立信號(hào))。為了防止競(jìng)爭(zhēng),條件變量的使用總是和一個(gè)互斥鎖結(jié)合在一起。
pthread_cond_t
pthread_cond_init
pthread_cond_signal
pthread_cond_wait
pthread_cond_broadcast
pthread_cond_wait的使用
pthread_cond_wait總是和一個(gè)互斥量同時(shí)使用的,我們看看APUE中關(guān)于pthread_cond_wait使用的原話:
傳遞給pthread_cond_wait的互斥量對(duì)條件(condition)進(jìn)行保護(hù)。調(diào)用者把鎖住的互斥量傳給函數(shù),函數(shù)然后自動(dòng)把調(diào)用線程放在等待條件(condition)的線程列表上,對(duì)互斥量進(jìn)行解鎖。這就關(guān)閉了條件檢查和線程進(jìn)入休眠狀態(tài)等待條件改變這兩個(gè)操作之間的時(shí)間通道,線程就不會(huì)錯(cuò)過(guò)條件(condition)的任何變化。
這段話說(shuō)明了為什么需要互斥量來(lái)保護(hù)條件變量,因?yàn)?strong>條件檢查和線程進(jìn)入休眠狀態(tài)等待條件改變這兩個(gè)操作都不是線程安全的,所以需要互斥量進(jìn)行保護(hù)。
pthread_mutex_lock(&mutex);
pthread_cond_wait(&condition, &mutex);
pthread_mutex_unlock(&mutex);
我們?cè)賮?lái)看看互斥量傳參進(jìn)入pthread_cond_wait時(shí),pthread_cond_wait對(duì)互斥量做了什么操作。再來(lái)回味一下上面APUE的這段話:
調(diào)用者把鎖住的互斥量傳給函數(shù),函數(shù)然后自動(dòng)把調(diào)用線程放在等待條件(condition)的線程列表上,對(duì)互斥量進(jìn)行解鎖。
也就是pthread_cond_wait里面做了兩個(gè)操作,第一個(gè)操作是將調(diào)用線程放在等待條件的線程列表上;第二個(gè)操作是將互斥量解鎖。
注意,將互斥量解鎖以后pthread_cond_wait并沒(méi)有返回,而是等待條件成立,等條件成立時(shí),pthread_cond_wait會(huì)再次獲得互斥量。
所以pthread_cond_wait對(duì)互斥量的操作實(shí)際上是這樣子的。
| -- pthread_mutex_lock lock 互斥量 (1)
|
| -- 進(jìn)入pthread_cond_wait函數(shù)邏輯
| -- unlock 互斥量 (2)
|
| -- 等待條件成立
|
| -- lock 互斥量 (3)
|
| -- 退出pthread_cond_wait函數(shù)邏輯
|
| -- pthread_mutex_unlock unlock 互斥量 (4)
在這個(gè)過(guò)程中,互斥量實(shí)際上是已經(jīng)被鎖住了兩次。
我們編寫(xiě)一個(gè)死鎖的例子舉證一下上面的過(guò)程,在這個(gè)例子我們驗(yàn)證pthread_cond_wait是否會(huì)lock互斥量,進(jìn)入pthread_cond_wait時(shí),互斥量是沒(méi)有鎖住的,在pthread_cond_wait返回之后,別的線程嘗試獲取鎖,如果卡死,那便說(shuō)明pthread_cond_wait里面有l(wèi)ock互斥量的操作。
#include <iostream>
#include <unistd.h>
#include <pthread.h>
using namespace std;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* test(void *p){
pthread_mutex_lock(&mutex); // (1)
pthread_mutex_unlock(&mutex); // (4)
cout << "child thread wait condition. " << endl;
pthread_cond_wait(&cond, &mutex); // (2) (3)
cout << "child thread get condition. " << endl;
cout << "child thread return." << endl;
// pthread_mutex_unlock(&mutex);
}
int main()
{
pthread_t tid;
pthread_create(&tid, NULL, test, NULL);
sleep(1); // 保證子線程先啟動(dòng)
cout << "main thread signal condition. " << endl;
pthread_cond_signal(&cond);
sleep(1); // 保證子線程能夠先得到收到條件并處理完
cout << "main thread acquire lock. " << endl;
pthread_mutex_lock(&mutex); // 嘗試獲取鎖
cout << "main thread acquire lock success. " << endl;
}
/* =========================================================================
* 輸出
*
* child thread wait condition.
* main thread signal condition.
* child thread get condition.
* child thread return.
* main thread acquire lock.
*
* =========================================================================
*/
在子線程返回后,主線程會(huì)一直卡在acquire lock這里,不會(huì)success,說(shuō)明在pthread_cond_wait里面確實(shí)有加鎖的操作,此時(shí)如沒(méi)有釋放鎖,其他線程獲取鎖將會(huì)進(jìn)入死鎖狀態(tài),所以在pthread_cond_wait使用前后,一定要使用pthread_mutex_lock和pthread_mutex_unlock。
pthread自旋鎖(spinlock)
自旋鎖與互斥鎖比較類似,它們都是為了解決對(duì)某項(xiàng)資源的互斥使用。無(wú)論是互斥鎖,還是自旋鎖,在任何時(shí)刻,最多只能有一個(gè)保持者,也就說(shuō),在任何時(shí)刻最多只能有一個(gè)執(zhí)行單元獲得鎖。但是兩者在調(diào)度機(jī)制上略有不同。對(duì)于互斥鎖,如果資源已經(jīng)被占用,資源申請(qǐng)者只能進(jìn)入睡眠狀態(tài)。但是自旋鎖不會(huì)引起調(diào)用者睡眠,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者就一直循環(huán)在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,"自旋"一詞就是因此而得名。
pthread_spinlock_t
pthread_spin_init
pthread_spin_destroy
pthread_spin_lock
pthread_spin_trylock
pthread_spin_unlock
pthread讀寫(xiě)鎖(rwlock)
讀寫(xiě)鎖實(shí)際是一種特殊的自旋鎖,它把對(duì)共享資源的訪問(wèn)者劃分成讀者和寫(xiě)者,讀者只對(duì)共享資源進(jìn)行讀訪問(wèn),寫(xiě)者則需要對(duì)共享資源進(jìn)行寫(xiě)操作。這種鎖相對(duì)于自旋鎖而言,能提高并發(fā)性,因?yàn)樵诙嗵幚砥飨到y(tǒng)中,它允許同時(shí)有多個(gè)讀者來(lái)訪問(wèn)共享資源,最大可能的讀者數(shù)為實(shí)際的邏輯CPU數(shù)。寫(xiě)者是排他性的,一個(gè)讀寫(xiě)鎖同時(shí)只能有一個(gè)寫(xiě)者或多個(gè)讀者(與CPU數(shù)相關(guān)),但不能同時(shí)既有讀者又有寫(xiě)者。
pthread_rwlock_t
pthread_rwlock_init
pthread_rwlock_wrlock
pthread_rwlock_rdlock
pthread_rwlock_unlock
pthread_rwlock_tryrdlock
pthread_rwlock_trywdlock
pthread_rwlock_destroy
pthread柵欄(barrier)
柵欄(Barrier)是并行計(jì)算中的一種同步方法。對(duì)于一群進(jìn)程或線程,程序中的一個(gè)同步屏障意味著任何線程/進(jìn)程執(zhí)行到此后必須等待,直到所有線程/進(jìn)程都到達(dá)此點(diǎn)才可繼續(xù)執(zhí)行下文。