国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Linux】Linux線程互斥與同步

這篇具有很好參考價值的文章主要介紹了【Linux】Linux線程互斥與同步。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

一、Linux線程互斥

1.進(jìn)程線程間的互斥相關(guān)背景概念

臨界資源:多線程執(zhí)行流共享的資源就叫做臨界資源

臨界區(qū):每個線程內(nèi)部,訪問臨界資源的代碼,就叫做臨界區(qū)

互斥:任何時刻,互斥保證有且只有一個執(zhí)行流進(jìn)入臨界區(qū),訪問臨界資源,通常對臨界資源起保護(hù)作用

原子性:不會被任何調(diào)度機(jī)制打斷的操作,該操作只有兩態(tài),要么完成,要么未完成

大部分情況,線程使用的數(shù)據(jù)都是局部變量,變量的地址空間在線程??臻g內(nèi),這種情況,變量歸屬單個線程,其他線程無法獲得這種變量。

但有時候,很多變量都需要在線程間共享,這樣的變量稱為共享變量,可以通過數(shù)據(jù)的共享,完成線程之間的交互。

多個線程并發(fā)的操作共享變量,會帶來一些問題。

我們來看下面搶票的代碼:

#include <iostream>
#include <vector>
#include <unistd.h>
#include <pthread.h>

int tickets = 10000;

void *getTicket(void *args)
{
    std::string threadname = static_cast<const char *>(args);
    while (true)
    {
        if (tickets > 0)
        {
            usleep(1000);
            std::cout << threadname << " 正在進(jìn)行搶票: " << tickets << std::endl;
            tickets--;
        }
        else
        {
            break;
        }
    }

    return nullptr;
}

int main()
{
    pthread_t t1, t2, t3, t4;
    pthread_create(&t1, nullptr, getTicket, (void *)"thread 1");
    pthread_create(&t2, nullptr, getTicket, (void *)"thread 2");
    pthread_create(&t3, nullptr, getTicket, (void *)"thread 3");
    pthread_create(&t4, nullptr, getTicket, (void *)"thread 4");

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    pthread_join(t3, nullptr);
    pthread_join(t4, nullptr);
    return 0;
}

【Linux】Linux線程互斥與同步,Linux系統(tǒng)編程,linux,java,線程互斥,線程同步,互斥鎖,條件變量,可重入與線程安全

我們發(fā)現(xiàn)最終票的數(shù)量是負(fù)數(shù),這顯然是不合理的

為什么可能無法獲得爭取結(jié)果?

if 語句判斷條件為真以后,代碼可以并發(fā)的切換到其他線程

usleep 這個模擬漫長業(yè)務(wù)的過程,在這個漫長的業(yè)務(wù)過程中,可能有很多個線程會進(jìn)入該代碼段

–ticket 操作本身就不是一個原子操作

取出ticket--部分的匯編代碼
objdump -d a.out > test.objdump
152 40064b: 8b 05 e3 04 20 00 mov 0x2004e3(%rip),%eax # 600b34 <ticket>
153 400651: 83 e8 01 sub $0x1,%eax
154 400654: 89 05 da 04 20 00 mov %eax,0x2004da(%rip) # 600b34 <ticket>

– 操作并不是原子操作,而是對應(yīng)三條匯編指令:

load :將共享變量ticket從內(nèi)存加載到寄存器中

update : 更新寄存器里面的值,執(zhí)行-1操作

store :將新值,從寄存器寫回共享變量ticket的內(nèi)存地址

對變量進(jìn)行++,或者–,在C、C++上,看起來只有一條語句,但是匯編之后至少是三條語句:

1.從內(nèi)存讀取數(shù)據(jù)到CPU寄存器中

⒉.在寄存器中讓CPU進(jìn)行對應(yīng)的算邏運(yùn)算

3.寫回新的結(jié)果到內(nèi)存中變量的位置

要解決以上問題,需要做到三點(diǎn):

代碼必須要有互斥行為:當(dāng)代碼進(jìn)入臨界區(qū)執(zhí)行時,不允許其他線程進(jìn)入該臨界區(qū)。

如果多個線程同時要求執(zhí)行臨界區(qū)的代碼,并且臨界區(qū)沒有線程在執(zhí)行,那么只能允許一個線程進(jìn)入該臨界區(qū)。

如果線程不在臨界區(qū)中執(zhí)行,那么該線程不能阻止其他線程進(jìn)入臨界區(qū)。

要做到這三點(diǎn),本質(zhì)上就是需要一把鎖。Linux上提供的這把鎖叫互斥量。

【Linux】Linux線程互斥與同步,Linux系統(tǒng)編程,linux,java,線程互斥,線程同步,互斥鎖,條件變量,可重入與線程安全

我們定義的全局變量,在沒有保護(hù)的時候,往往是不安全的,像上面多個線程在交替執(zhí)行造成的數(shù)據(jù)安全問題,發(fā)生了數(shù)據(jù)不一致問題!

2.互斥量的接口

初始化互斥量

初始化互斥量有兩種方法:

方法1,靜態(tài)分配:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

方法2,動態(tài)分配:

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrictattr);
參數(shù):
mutex:要初始化的互斥量
attr:NULL

銷毀互斥量

銷毀互斥量需要注意:

使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要銷毀

不要銷毀一個已經(jīng)加鎖的互斥量

已經(jīng)銷毀的互斥量,要確保后面不會有線程再嘗試加鎖

互斥量加鎖和解鎖

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失敗返回錯誤號

調(diào)用 pthread_ lock 時,可能會遇到以下情況:

互斥量處于未鎖狀態(tài),該函數(shù)會將互斥量鎖定,同時返回成功

發(fā)起函數(shù)調(diào)用時,其他線程已經(jīng)鎖定互斥量,或者存在其他線程同時申請互斥量,但沒有競爭到互斥量,那么pthread_ lock調(diào)用會陷入阻塞(執(zhí)行流被掛起),等待互斥量解鎖。

1.使用靜態(tài)分配的鎖–全局的鎖

#include <iostream>
#include <vector>
#include <unistd.h>
#include <pthread.h>

int tickets = 10000;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void *getTicket(void *args)
{
    std::string threadname = static_cast<const char *>(args);
    while (true)
    {
        pthread_mutex_lock(&mutex);
        if (tickets > 0)
        {
            usleep(1000);
            std::cout << threadname << " 正在進(jìn)行搶票: " << tickets << std::endl;
            tickets--;
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
    }

    return nullptr;
}

int main()
{
    pthread_t t1, t2, t3, t4;
    pthread_create(&t1, nullptr, getTicket, (void *)"thread 1");
    pthread_create(&t2, nullptr, getTicket, (void *)"thread 2");
    pthread_create(&t3, nullptr, getTicket, (void *)"thread 3");
    pthread_create(&t4, nullptr, getTicket, (void *)"thread 4");

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    pthread_join(t3, nullptr);
    pthread_join(t4, nullptr);
    return 0;
}

【Linux】Linux線程互斥與同步,Linux系統(tǒng)編程,linux,java,線程互斥,線程同步,互斥鎖,條件變量,可重入與線程安全

2.使用動態(tài)分配的鎖

#include <iostream>
#include <vector>
#include <unistd.h>
#include <pthread.h>

int tickets = 10000;

class threadData
{
public:
    threadData(const std::string &threadname, pthread_mutex_t *mutex_p)
        : _threadname(threadname), _mutex_p(mutex_p)
    {
    }
    ~threadData() {}

public:
    std::string _threadname;
    pthread_mutex_t *_mutex_p;
};

void *getTicket(void *args)
{
    threadData *td = static_cast<threadData *>(args);
    while (true)
    {
        pthread_mutex_lock(td->_mutex_p);
        if (tickets > 0)
        {
            usleep(1000);
            std::cout << td->_threadname << " 正在進(jìn)行搶票: " << tickets << std::endl;
            tickets--;
            pthread_mutex_unlock(td->_mutex_p);
        }
        else
        {
            pthread_mutex_unlock(td->_mutex_p);
            break;
        }
    }

    return nullptr;
}

int main()
{
#define NUM 4
    pthread_mutex_t lock;
    pthread_mutex_init(&lock, nullptr);

    std::vector<pthread_t> tids(NUM);
    for (int i = 0; i < NUM; i++)
    {
        char buffer[64];
        snprintf(buffer, sizeof buffer, "thread %d", i + 1);
        threadData *td = new threadData(buffer, &lock);
        pthread_create(&tids[i], nullptr, getTicket, td);
    }

    for (int i = 0; i < NUM; i++)
    {
        pthread_join(tids[i], nullptr);
    }
    return 0;
}

【Linux】Linux線程互斥與同步,Linux系統(tǒng)編程,linux,java,線程互斥,線程同步,互斥鎖,條件變量,可重入與線程安全

我們發(fā)現(xiàn)在我們進(jìn)行加鎖之后,程序運(yùn)行變慢了,這是因?yàn)樵谂R界區(qū)中程序都是串行執(zhí)行的

加鎖和解鎖的過程多個線程串行執(zhí)行的,程序變慢了!
鎖只規(guī)定互斥訪問,沒有規(guī)定必須讓誰優(yōu)先執(zhí)行
鎖就是真是的讓多個執(zhí)行流進(jìn)行競爭的結(jié)果

  1. 如何看待鎖
a. 鎖,本身就是一個共享資源!全局的變量是要被保護(hù)的,鎖是用來保護(hù)全局的資源的,鎖本身也是全局資源,鎖的安全誰來保護(hù)呢?
b. pthread_mutex_lock、pthread_mutex_unlock:加鎖的過程必須是安全的!加鎖的過程其實(shí)是原子的!
c. 如果申請成功,就繼續(xù)向后執(zhí)行,如果申請暫時沒有成功,執(zhí)行流會阻塞!
d. 誰持有鎖,誰進(jìn)入臨界區(qū)!
  1. 如何理解加鎖和解鎖的本質(zhì) — 加鎖的過程是原子的!

如果線程1,申請鎖成功,進(jìn)入臨界資源,正在訪問臨界資源期間,其他線程在做什么?﹖阻塞等待

如果線程1,申請鎖成功,進(jìn)入臨界資源,正在訪問臨界資源期間,我可不可以被切換呢??絕對可以的!

當(dāng)持有鎖的線程被切走的時候,是抱著鎖被切走的,即便自己被切走了,其他線程依舊無法申請鎖成功,也便無法向后執(zhí)行!直到我最終釋放這個鎖!

所以,對于其他線程而言,有意義的鎖的狀態(tài),無非兩種1.申請鎖前⒉釋放鎖后

站在其他線程的角度,看待當(dāng)前線程持有鎖的過程,就是原子的!!

未來我們在使用鎖的時候,一定要盡量保證臨界區(qū)的粒度要非常小! 粒度:鎖中間保護(hù)代碼的多少

3.互斥量實(shí)現(xiàn)原理

經(jīng)過上面的例子,大家已經(jīng)意識到單純的 sticket++ 或者sticket-- 都不是原子的,有可能會有數(shù)據(jù)一致性問題

為了實(shí)現(xiàn)互斥鎖操作,大多數(shù)體系結(jié)構(gòu)都提供了swap或exchange指令,該指令的作用是把寄存器和內(nèi)存單元的數(shù)據(jù)相交換,由于只有一條指令,保證了原子性,即使是多處理器平臺,訪問內(nèi)存的 總線周期也有先后,一個處理器上的交換指令執(zhí)行時另一個處理器的交換指令只能等待總線周期。 現(xiàn)在我們把lock和unlock的偽代碼改一下

【Linux】Linux線程互斥與同步,Linux系統(tǒng)編程,linux,java,線程互斥,線程同步,互斥鎖,條件變量,可重入與線程安全

1.CPU內(nèi)寄存器只有一套被所有執(zhí)行流共享

2.CPU內(nèi)寄存器的內(nèi)容,是每個執(zhí)行流私有的,運(yùn)行時上下文

假如線程1先執(zhí)行,先申請鎖,將0放到寄存器中,然后將鎖的值和寄存器中的值進(jìn)行交換,即使現(xiàn)在進(jìn)行被切換,那么寄存器中的值作為上下文數(shù)據(jù)也會被線程1帶走,那么下一個線程來的時候,將寄存器中的值與內(nèi)存中鎖的值進(jìn)行交換,那么寄存器中的值還是0,線程就會掛起等待,只有線程1執(zhí)行完畢之后釋放鎖,其他線程才能夠申請到鎖,就保存了申請鎖的原子性。交換的本質(zhì):共享的數(shù)據(jù),交換到我得上下文中!!!一條匯編完成I

釋放鎖的時候,將1賦值給鎖的值,喚醒其他線程,就完成了釋放鎖的功能。

4.可重入VS線程安全

4.1.可重入和線程安全的概念

線程安全:多個線程并發(fā)同一段代碼時,不會出現(xiàn)不同的結(jié)果。常見對全局變量或者靜態(tài)變量進(jìn)行操作,并且沒有鎖保護(hù)的情況下,會出現(xiàn)該問題。

重入:同一個函數(shù)被不同的執(zhí)行流調(diào)用,當(dāng)前一個流程還沒有執(zhí)行完,就有其他的執(zhí)行流再次進(jìn)入,我們稱之為重入。一個函數(shù)在重入的情況下,運(yùn)行結(jié)果不會出現(xiàn)任何不同或者任何問題,則該函數(shù)被稱為可重入函數(shù),否則,是不可重入函數(shù)。

4.2常見的線程不安全的情況

不保護(hù)共享變量的函數(shù)

函數(shù)狀態(tài)隨著被調(diào)用,狀態(tài)發(fā)生變化的函數(shù)

返回指向靜態(tài)變量指針的函數(shù)

調(diào)用線程不安全函數(shù)的函數(shù)

4.3常見的線程安全的情況

每個線程對全局變量或者靜態(tài)變量只有讀取的權(quán)限,而沒有寫入的權(quán)限,一般來說這些線程是安全的

類或者接口對于線程來說都是原子操作

多個線程之間的切換不會導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性

4.4常見不可重入的情況

調(diào)用了malloc/free函數(shù),因?yàn)閙alloc函數(shù)是用全局鏈表來管理堆的

調(diào)用了標(biāo)準(zhǔn)I/O庫函數(shù),標(biāo)準(zhǔn)I/O庫的很多實(shí)現(xiàn)都以不可重入的方式使用全局?jǐn)?shù)據(jù)結(jié)構(gòu)

可重入函數(shù)體內(nèi)使用了靜態(tài)的數(shù)據(jù)結(jié)構(gòu)

4.5常見可重入的情況

不使用全局變量或靜態(tài)變量

不使用用malloc或者new開辟出的空間

不調(diào)用不可重入函數(shù)

不返回靜態(tài)或全局?jǐn)?shù)據(jù),所有數(shù)據(jù)都有函數(shù)的調(diào)用者提供

使用本地?cái)?shù)據(jù),或者通過制作全局?jǐn)?shù)據(jù)的本地拷貝來保護(hù)全局?jǐn)?shù)據(jù)

4.6可重入與線程安全聯(lián)系

函數(shù)是可重入的,那就是線程安全的

函數(shù)是不可重入的,那就不能由多個線程使用,有可能引發(fā)線程安全問題

如果一個函數(shù)中有全局變量,那么這個函數(shù)既不是線程安全也不是可重入的。

4.7可重入與線程安全區(qū)別

可重入函數(shù)是線程安全函數(shù)的一種

線程安全不一定是可重入的,而可重入函數(shù)則一定是線程安全的。

如果將對臨界資源的訪問加上鎖,則這個函數(shù)是線程安全的,但如果這個重入函數(shù)若鎖還未釋放則會產(chǎn)生死鎖,因此是不可重入的。

5.死鎖

5.1死鎖的概念

死鎖是指在一組進(jìn)程中的各個進(jìn)程均占有不會釋放的資源,但因互相申請被其他進(jìn)程所站用不會釋放的資源而處于的一種永久等待狀態(tài)。

5.2死鎖四個必要條件

互斥條件:一個資源每次只能被一個執(zhí)行流使用

請求與保持條件:一個執(zhí)行流因請求資源而阻塞時,對已獲得的資源保持不放

不剝奪條件:一個執(zhí)行流已獲得的資源,在末使用完之前,不能強(qiáng)行剝奪

循環(huán)等待條件:若干執(zhí)行流之間形成一種頭尾相接的循環(huán)等待資源的關(guān)系

5.3如何避免死鎖

破壞死鎖的四個必要條件

加鎖順序一致

避免鎖未釋放的場景

資源一次性分配

5.4避免死鎖算法

死鎖檢測算法(了解)

銀行家算法(了解)

總結(jié):

談?wù)勊梨i:在多把鎖的場景下,我們持有自己的鎖不釋放,還要對方的鎖,對方也是如此,此時就容易造成死鎖!1.一把鎖,有可能死鎖嗎?不可能

2.為什么會有死鎖?邏輯鏈條

一定是你用了鎖<–為什么你要用鎖呢<–保證臨界資源的安全<-多錢程訪問我們可能出現(xiàn)數(shù)據(jù)不一致的問題<–多線程&全局資源<–多戰(zhàn)程大部分資源(全局的)是共享的<–多線程的特性

任何技術(shù)都有自己的邊界,是解決問題的,但有可能在解決問題的同時,一定會可能引入新的問題!

二、 Linux線程同步

1.同步概念與競態(tài)條件

同步:在保證數(shù)據(jù)安全的前提下,讓線程能夠按照某種特定的順序訪問臨界資源,從而有效避免饑餓問題,叫做同步

競態(tài)條件:因?yàn)闀r序問題,而導(dǎo)致程序異常,我們稱之為競態(tài)條件。在線程場景下,這種問題也不難理解

2.條件變量

當(dāng)一個線程互斥地訪問某個變量時,它可能發(fā)現(xiàn)在其它線程改變狀態(tài)之前,它什么也做不了。

例如一個線程訪問隊(duì)列時,發(fā)現(xiàn)隊(duì)列為空,它只能等待,只到其它線程將一個節(jié)點(diǎn)添加到隊(duì)列中。這種情

況就需要用到條件變量。

3.條件變量函數(shù)

初始化

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict
attr);
參數(shù):
cond:要初始化的條件變量
attr:NULL

銷毀

int pthread_cond_destroy(pthread_cond_t *cond)

等待條件滿足

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
參數(shù):
cond:要在這個條件變量上等待
mutex:互斥量

喚醒等待

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.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;
        tickets--;
        pthread_mutex_unlock(&mutex);
    }
}

int main()
{
    // 通過條件變量控制線程的執(zhí)行
    pthread_t t[5];
    for (int i = 0; i < 5; i++)
    {
        char *name = new char[64];
        snprintf(name, 64, "thread %d", i + 1);
        pthread_create(t+i, nullptr, start_routine, name);
    }

    while (true)
    {
        sleep(1);
        pthread_cond_signal(&cond);
        std::cout << "main thread wakeup one thread..." << std::endl;
    }
    for (int i = 0; i < 5; i++)
    {
        pthread_join(t[i], nullptr);
    }

    return 0;
}

【Linux】Linux線程互斥與同步,Linux系統(tǒng)編程,linux,java,線程互斥,線程同步,互斥鎖,條件變量,可重入與線程安全

pthread_cond_wait函數(shù),線程會進(jìn)入阻塞狀態(tài),并且會自動釋放已經(jīng)持有的互斥鎖。這樣其他線程就可以獲取到互斥鎖,并繼續(xù)執(zhí)行。等待條件變量的線程被喚醒后,它會重新獲取互斥鎖,然后繼續(xù)往下執(zhí)行。

在主線程中,通過調(diào)用pthread_cond_signal函數(shù),會喚醒一個等待條件變量的線程。每個線程都會嘗試重新獲取互斥鎖并繼續(xù)執(zhí)行。這樣就實(shí)現(xiàn)了多個線程同時競爭互斥鎖的機(jī)制。

因此,雖然只有一個線程能夠獲取到互斥鎖并進(jìn)入臨界區(qū),但其他線程也有機(jī)會競爭互斥鎖,而不是一直被阻塞。這樣可以提高并發(fā)性能,讓多個線程能夠同時執(zhí)行而不是串行執(zhí)行。

我們也可以一次喚醒所有的線程:

pthread_cond_broadcast(pthread_cond_t *cond);

結(jié)果運(yùn)行如下:

【Linux】Linux線程互斥與同步,Linux系統(tǒng)編程,linux,java,線程互斥,線程同步,互斥鎖,條件變量,可重入與線程安全

為什么 pthread_cond_wait 需要互斥量?

條件等待是線程間同步的一種手段,如果只有一個線程,條件不滿足,一直等下去都不會滿足,所以必須要有一個線程通過某些操作,改變共享變量,使原先不滿足的條件變得滿足,并且友好的通知等待在條件變量上的線程。

條件不會無緣無故的突然變得滿足了,必然會牽扯到共享數(shù)據(jù)的變化。所以一定要用互斥鎖來保護(hù)。沒有互斥鎖就無法安全的獲取和修改共享數(shù)據(jù)。

【Linux】Linux線程互斥與同步,Linux系統(tǒng)編程,linux,java,線程互斥,線程同步,互斥鎖,條件變量,可重入與線程安全

按照上面的說法,我們設(shè)計(jì)出如下的代碼:先上鎖,發(fā)現(xiàn)條件不滿足,解鎖,然后等待在條件變量上不就行了,如下代碼

// 錯誤的設(shè)計(jì)
pthread_mutex_lock(&mutex);
while (condition_is_false)
{
	pthread_mutex_unlock(&mutex);
	//解鎖之后,等待之前,條件可能已經(jīng)滿足,信號已經(jīng)發(fā)出,但是該信號可能被錯過
	pthread_cond_wait(&cond);
	pthread_mutex_lock(&mutex);
}
pthread_mutex_unlock(&mutex);

由于解鎖和等待不是原子操作。調(diào)用解鎖之后, pthread_cond_wait 之前,如果已經(jīng)有其他線程獲取到互斥量,摒棄條件滿足,發(fā)送了信號,那么 pthread_cond_wait 將錯過這個信號,可能會導(dǎo)致線程永遠(yuǎn)阻塞在這個 pthread_cond_wait 。所以解鎖和等待必須是一個原子操作。int pthread_cond_wait(pthread_cond_ t *cond,pthread_mutex_ t * mutex); 進(jìn)入該函數(shù)后,會去看條件量等于0不?等于,就把互斥量變成1,直到cond_ wait返回,把條件量改成1,把互斥量恢復(fù)成原樣。

pthread_cond_wait函數(shù)的操作可以分為以下幾步:

  1. 當(dāng)線程調(diào)用pthread_cond_wait函數(shù)時,它會首先將自己加入到條件變量的等待隊(duì)列中,并釋放已經(jīng)持有的互斥鎖。這樣其他線程就能夠獲取到互斥鎖并繼續(xù)執(zhí)行,而不會因?yàn)榛コ怄i一直被該線程占用而導(dǎo)致死鎖問題。
  2. 當(dāng)條件變量發(fā)出信號(如通過pthread_cond_signalpthread_cond_broadcast函數(shù))喚醒一個或多個等待在條件變量上的線程時,被喚醒的線程會嘗試重新獲取互斥鎖。
  3. 一旦線程獲取到互斥鎖,它就可以繼續(xù)執(zhí)行臨界區(qū)的代碼。

如果沒有互斥鎖的配合,pthread_cond_wait函數(shù)無法正確釋放和重新獲取互斥鎖,這可能導(dǎo)致死鎖或競態(tài)條件問題。通過使用互斥鎖,pthread_cond_wait函數(shù)可以安全地釋放和重新獲取互斥鎖,確保線程之間的同步和順序執(zhí)行。因此,在使用條件變量時,通常需要與互斥鎖配對使用以確保正確的同步機(jī)制。

4.條件變量使用規(guī)范

等待條件代碼

pthread_mutex_lock(&mutex);
while (條件為假)
pthread_cond_wait(cond, mutex);
修改條件
pthread_mutex_unlock(&mutex);
  1. 線程調(diào)用pthread_mutex_lock函數(shù)獲取互斥鎖,如果互斥鎖已經(jīng)被其他線程占用,則當(dāng)前線程將進(jìn)入阻塞狀態(tài),直到能夠獲取到互斥鎖為止。
  2. 然后線程進(jìn)入一個while循環(huán),判斷是否滿足特定的條件。如果不滿足,則線程調(diào)用pthread_cond_wait函數(shù)進(jìn)入阻塞狀態(tài),并且會自動釋放已經(jīng)持有的互斥鎖。等待條件變量的線程被喚醒后,它會重新獲取互斥鎖并繼續(xù)往下執(zhí)行。
  3. 當(dāng)線程被喚醒時,它會再次檢查while循環(huán)的條件是否滿足。如果不滿足,則線程會繼續(xù)阻塞等待。
  4. 如果while循環(huán)的條件滿足,則線程會執(zhí)行修改條件的代碼,然后調(diào)用pthread_mutex_unlock函數(shù)釋放互斥鎖。

通過這種方式,線程可以等待某些特定的條件滿足后再繼續(xù)執(zhí)行。同時,它也可以避免競態(tài)條件和死鎖問題的出現(xiàn)。

給條件發(fā)送信號代碼

pthread_mutex_lock(&mutex);
設(shè)置條件為真
pthread_cond_signal(cond);
pthread_mutex_unlock(&mutex);
  1. 線程調(diào)用pthread_mutex_lock函數(shù)獲取互斥鎖,如果互斥鎖已經(jīng)被其他線程占用,則當(dāng)前線程將進(jìn)入阻塞狀態(tài),直到能夠獲取到互斥鎖為止。
  2. 然后設(shè)置條件為真,即滿足了特定的條件。
  3. 接著,線程調(diào)用pthread_cond_signal函數(shù),向等待在條件變量上的一個線程發(fā)出信號,告訴它可以繼續(xù)執(zhí)行了。
  4. 最后,線程調(diào)用pthread_mutex_unlock函數(shù)釋放互斥鎖。

通過這樣的操作,線程在滿足特定條件后,可以通過條件變量和互斥鎖來實(shí)現(xiàn)與其他線程之間的同步。其中,pthread_cond_signal函數(shù)用于通知等待在條件變量上的一個線程,而pthread_cond_broadcast函數(shù)可以通知所有等待在條件變量上的線程。這樣可以確保等待在條件變量上的線程能夠及時被喚醒并繼續(xù)執(zhí)行。文章來源地址http://www.zghlxwxcb.cn/news/detail-761639.html

到了這里,關(guān)于【Linux】Linux線程互斥與同步的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Linux-線程的同步與互斥

    Linux-線程的同步與互斥

    ?? 臨界資源:多線程指行流共享的資源叫做臨界資源。 ?? 臨界區(qū):每個線程內(nèi)部訪問臨界資源的代碼片段叫做臨界區(qū)。 ?? 互斥:任何時刻,互斥保證只有一個指行流進(jìn)入臨界區(qū),訪問臨界資源,通常是對臨界區(qū)起保護(hù)作用。 ?? 原子性:不被任何調(diào)度所打斷的操作,該

    2024年02月09日
    瀏覽(18)
  • Linux——多線程,互斥與同步

    Linux——多線程,互斥與同步

    目錄 一.linux互斥 1.進(jìn)程線程間的互斥相關(guān)背景概念 2.互斥量mutex 3.加鎖互斥鎖mutex 4.鎖的底層原理 ?二.可重入VS線程安全 1.概念 2.常見的線程不安全的情況 3.常見的線程安全的情況? 4.常見不可重入的情況? 5..常見可重入的情況 6.可重入與線程安全聯(lián)系 ?三.死鎖 1.死鎖四個必

    2024年02月05日
    瀏覽(19)
  • Linux——線程的同步與互斥

    Linux——線程的同步與互斥

    目錄 模擬搶火車票的過程 代碼示例 thread.cc Thread.hpp 運(yùn)行結(jié)果 分析原因 tickets減到-2的本質(zhì)? 解決搶票出錯的方案 臨界資源的概念 原子性的概念 加鎖 定義 初始化 銷毀 代碼形式如下 代碼示例1: 代碼示例2: 總結(jié) 如何看待鎖 申請失敗將會阻塞 ?pthread_mutex_tyrlock 互斥鎖實(shí)現(xiàn)

    2024年02月06日
    瀏覽(27)
  • 【Linux】多線程互斥與同步

    【Linux】多線程互斥與同步

    互斥 指的是一種機(jī)制,用于確保在同一時刻只有一個進(jìn)程或線程能夠訪問共享資源或執(zhí)行臨界區(qū)代碼。 互斥的目的是 防止多個并發(fā)執(zhí)行的進(jìn)程或線程訪問共享資源時產(chǎn)生競爭條件,從而保證數(shù)據(jù)的一致性和正確性 ,下面我們來使用多線程來模擬實(shí)現(xiàn)一個搶票的場景,看看所

    2024年02月09日
    瀏覽(16)
  • 『Linux』第九講:Linux多線程詳解(三)_ 線程互斥 | 線程同步

    『Linux』第九講:Linux多線程詳解(三)_ 線程互斥 | 線程同步

    「前言」文章是關(guān)于Linux多線程方面的知識,上一篇是?Linux多線程詳解(二),今天這篇是 Linux多線程詳解(三),內(nèi)容大致是線程互斥與線程同步,講解下面開始! 「歸屬專欄」Linux系統(tǒng)編程 「主頁鏈接」個人主頁 「筆者」楓葉先生(fy) 「楓葉先生有點(diǎn)文青病」「每篇一句

    2024年02月02日
    瀏覽(23)
  • 【關(guān)于Linux中----線程互斥與同步】

    【關(guān)于Linux中----線程互斥與同步】

    先來用代碼模擬一個搶票的場景,四個線程不停地?fù)屍?,一共?000張票,搶完為止,代碼如下: 執(zhí)行結(jié)果如下: 可以看到,最后出現(xiàn)了票數(shù)為負(fù)數(shù)的情況,很顯然這是錯誤的,是不應(yīng)該出現(xiàn)的。 為什么會出現(xiàn)這種情況? 首先要明確,上述的幾個線程是不能同時執(zhí)行搶票的

    2023年04月08日
    瀏覽(17)
  • 【Linux】多線程2——線程互斥與同步/多線程應(yīng)用

    【Linux】多線程2——線程互斥與同步/多線程應(yīng)用

    ??上文主要介紹了多線程之間的獨(dú)立資源,本文將詳細(xì)介紹多線程之間的 共享資源 存在的問題和解決方法。 intro 多線程共享進(jìn)程地址空間,包括創(chuàng)建的全局變量、堆、動態(tài)庫等。下面是基于全局變量實(shí)現(xiàn)的一個多線程搶票的demo。 發(fā)現(xiàn)錯誤:線程搶到負(fù)數(shù)編號的票,為什么

    2024年02月10日
    瀏覽(20)
  • 【Linux】多線程 --- 線程同步與互斥+生產(chǎn)消費(fèi)模型

    【Linux】多線程 --- 線程同步與互斥+生產(chǎn)消費(fèi)模型

    人生總是那么痛苦嗎?還是只有小時候是這樣? —總是如此 1. 假設(shè)現(xiàn)在有一份共享資源tickets,如果我們想讓多個線程都對這個資源進(jìn)行操作,也就是tickets- -的操作,但下面兩份代碼分別出現(xiàn)了不同的結(jié)果,上面代碼并沒有出現(xiàn)問題,而下面代碼卻出現(xiàn)了票為負(fù)數(shù)的情況,這

    2024年02月06日
    瀏覽(21)
  • Linux pthread線程操作 和 線程同步與互斥操作

    Linux pthread線程操作 和 線程同步與互斥操作

    在Linux系統(tǒng)中玩線程,使用pthread,這篇博客記錄如何 創(chuàng)建線程 和 使用線程 和線程的 同步 與 互斥 。 還有一份nginx線程池的代碼供大家閱讀學(xué)習(xí)! 目錄 一、簡介 什么是線程 線程的優(yōu)點(diǎn)、缺點(diǎn) 線程的應(yīng)用場合 二、線程的使用 1.? 創(chuàng)建線程 - pthread_create 2.? 線程的終止 - pt

    2024年02月02日
    瀏覽(23)
  • 線程同步-信號量-互斥量-條件變量

    線程同步-信號量-互斥量-條件變量

    線程同步其實(shí)實(shí)現(xiàn)的是線程排隊(duì)。 防止線程同步訪問共享資源造成沖突。 多個線程訪問共享資源的代碼有可能是同一份代碼,也有可能是不同的代碼;無論是否執(zhí)行同一份代碼,只要這些線程的代碼訪問同一份可變的共享資源,這些線程之間就需要同步。 1. 問題 同一個進(jìn)程

    2023年04月16日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包