pthread.h 相關(guān)函數(shù)使用方法集錦之線程同步變量

前言

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í)行下文。

pthread_barrier_t

pthread_barrier_init

pthread_barrier_wait

pthread_barrier_destroy

原文鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • linux編程-線程 MUTEX 一.概述 互斥量是線程同步的一...
    Aska偶陣雨閱讀 546評(píng)論 0 0
  • Q:為什么出現(xiàn)多線程? A:為了實(shí)現(xiàn)同時(shí)干多件事的需求(并發(fā)),同時(shí)進(jìn)行著下載和頁(yè)面UI刷新。對(duì)于處理器,為每個(gè)線...
    幸福相依閱讀 1,721評(píng)論 0 2
  • 線程 在linux內(nèi)核那一部分我們知道,線程其實(shí)就是一種特殊的進(jìn)程,只是他們共享進(jìn)程的文件和內(nèi)存等資源,無(wú)論如何對(duì)...
    大雄good閱讀 710評(píng)論 0 2
  • 線程 線程的概念 典型的UNIX進(jìn)程可以看成只有一個(gè)控制線程:一個(gè)進(jìn)程在同一時(shí)刻只做一件事。有了多個(gè)控制線程后,在...
    ColdWave閱讀 1,578評(píng)論 0 0
  • 1,2017年1月21日開(kāi)始吃地瓜餐,約20日后開(kāi)始膚色發(fā)黃,當(dāng)時(shí)吃的黃的多!65天停后,膚色漸漸變回來(lái)了!中間黃...
    南得糊涂呀閱讀 1,093評(píng)論 0 1

友情鏈接更多精彩內(nèi)容