前言
pthread(POSIX thread)骗奖,簡稱為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ī)制儿倒。通過鎖機(jī)制版保,能夠保證在多核多線程環(huán)境中,在某一個時間點(diǎn)上夫否,只能有一個線程進(jìn)入臨界區(qū)代碼彻犁,從而保證臨界區(qū)中操作數(shù)據(jù)的一致性。
在pthread里面實(shí)現(xiàn)了5中同步變量凰慈,分別是互斥鎖(mutex)汞幢、自旋鎖(spinlock)、條件變量(condition)溉瓶、讀寫鎖(rwlock)以及柵欄(barrier)急鳄。
線程同步變量
pthread互斥鎖(mutex)
互斥鎖是一個二元變量,其狀態(tài)為開鎖(允許0)和上鎖(禁止1)堰酿,將某個共享資源與某個特定互斥鎖在邏輯上綁定(要申請該資源必須先獲取鎖)。
訪問公共資源前张足,必須申請該互斥鎖触创,若處于開鎖狀態(tài),則申請到鎖對象为牍,并立即占有該鎖哼绑,以防止其他線程訪問該資源;如果該互斥鎖處于鎖定狀態(tài)碉咆,則阻塞當(dāng)前線程抖韩。
只有鎖定該互斥鎖的進(jìn)程才能釋放該互斥鎖,其他線程試圖釋放無效疫铜∶。互斥鎖使用的非常廣泛,在所有線程同步變量中壳咕,可以重點(diǎn)關(guān)注席揽。
pthread_mutex_t
鎖類型,定義互斥鎖谓厘。
pthread_mutex_init
初始化互斥鎖幌羞。
pthread_mutex_lock
申請互斥鎖并占有互斥鎖,其他線程無法訪問該資源竟稳。
pthread_mutex_trylock
pthread_mutex_unlock
釋放互斥鎖属桦,此時其他線程可以訪問該資源熊痴。
pthread_mutex_destroy
釋放鎖資源。
pthread條件變量(condition)
條件變量是利用線程間共享的全局變量進(jìn)行同步的一種機(jī)制聂宾,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起愁拭;另一個線程使"條件成立"(給出條件成立信號)。為了防止競爭亏吝,條件變量的使用總是和一個互斥鎖結(jié)合在一起岭埠。
pthread_cond_t
pthread_cond_init
pthread_cond_signal
pthread_cond_wait
pthread_cond_broadcast
pthread_cond_wait的使用
pthread_cond_wait總是和一個互斥量同時使用的,我們看看APUE
中關(guān)于pthread_cond_wait使用的原話:
傳遞給pthread_cond_wait的互斥量對條件(condition)進(jìn)行保護(hù)蔚鸥。調(diào)用者把鎖住的互斥量傳給函數(shù)惜论,函數(shù)然后自動把調(diào)用線程放在等待條件(condition)的線程列表上,對互斥量進(jìn)行解鎖止喷。這就關(guān)閉了條件檢查和線程進(jìn)入休眠狀態(tài)等待條件改變這兩個操作之間的時間通道馆类,線程就不會錯過條件(condition)的任何變化。
這段話說明了為什么需要互斥量來保護(hù)條件變量弹谁,因?yàn)?strong>條件檢查和線程進(jìn)入休眠狀態(tài)等待條件改變這兩個操作都不是線程安全的乾巧,所以需要互斥量進(jìn)行保護(hù)。
pthread_mutex_lock(&mutex);
pthread_cond_wait(&condition, &mutex);
pthread_mutex_unlock(&mutex);
我們再來看看互斥量傳參進(jìn)入pthread_cond_wait時预愤,pthread_cond_wait對互斥量做了什么操作沟于。再來回味一下上面APUE
的這段話:
調(diào)用者把鎖住的互斥量傳給函數(shù),函數(shù)然后自動把調(diào)用線程放在等待條件(condition)的線程列表上植康,對互斥量進(jìn)行解鎖旷太。
也就是pthread_cond_wait里面做了兩個操作,第一個操作是將調(diào)用線程放在等待條件的線程列表上销睁;第二個操作是將互斥量解鎖供璧。
注意,將互斥量解鎖以后pthread_cond_wait并沒有返回冻记,而是等待條件成立睡毒,等條件成立時,pthread_cond_wait會再次獲得互斥量冗栗。
所以pthread_cond_wait對互斥量的操作實(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)
在這個過程中,互斥量實(shí)際上是已經(jīng)被鎖住了兩次贞瞒。
我們編寫一個死鎖的例子舉證一下上面的過程偶房,在這個例子我們驗(yàn)證pthread_cond_wait是否會lock互斥量,進(jìn)入pthread_cond_wait時军浆,互斥量是沒有鎖住的棕洋,在pthread_cond_wait返回之后,別的線程嘗試獲取鎖乒融,如果卡死掰盘,那便說明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); // 保證子線程先啟動
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.
*
* =========================================================================
*/
在子線程返回后,主線程會一直卡在acquire lock這里愧捕,不會success奢驯,說明在pthread_cond_wait里面確實(shí)有加鎖的操作,此時如沒有釋放鎖次绘,其他線程獲取鎖將會進(jìn)入死鎖狀態(tài)瘪阁,所以在pthread_cond_wait使用前后,一定要使用pthread_mutex_lock和pthread_mutex_unlock邮偎。
pthread自旋鎖(spinlock)
自旋鎖與互斥鎖比較類似管跺,它們都是為了解決對某項(xiàng)資源的互斥使用。無論是互斥鎖禾进,還是自旋鎖豁跑,在任何時刻罗岖,最多只能有一個保持者单料,也就說,在任何時刻最多只能有一個執(zhí)行單元獲得鎖骏掀。但是兩者在調(diào)度機(jī)制上略有不同宠纯。對于互斥鎖卸夕,如果資源已經(jīng)被占用,資源申請者只能進(jìn)入睡眠狀態(tài)征椒。但是自旋鎖不會引起調(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讀寫鎖(rwlock)
讀寫鎖實(shí)際是一種特殊的自旋鎖治力,它把對共享資源的訪問者劃分成讀者和寫者蒙秒,讀者只對共享資源進(jìn)行讀訪問,寫者則需要對共享資源進(jìn)行寫操作宵统。這種鎖相對于自旋鎖而言晕讲,能提高并發(fā)性,因?yàn)樵诙嗵幚砥飨到y(tǒng)中马澈,它允許同時有多個讀者來訪問共享資源瓢省,最大可能的讀者數(shù)為實(shí)際的邏輯CPU數(shù)。寫者是排他性的痊班,一個讀寫鎖同時只能有一個寫者或多個讀者(與CPU數(shù)相關(guān))勤婚,但不能同時既有讀者又有寫者。
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ìn)程或線程馒胆,程序中的一個同步屏障意味著任何線程/進(jìn)程執(zhí)行到此后必須等待缨称,直到所有線程/進(jìn)程都到達(dá)此點(diǎn)才可繼續(xù)執(zhí)行下文。