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

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

這篇具有很好參考價(jià)值的文章主要介紹了『Linux』第九講:Linux多線程詳解(三)_ 線程互斥 | 線程同步。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

「前言」文章是關(guān)于Linux多線程方面的知識(shí),上一篇是?Linux多線程詳解(二),今天這篇是 Linux多線程詳解(三),內(nèi)容大致是線程互斥與線程同步,講解下面開始!

「歸屬專欄」Linux系統(tǒng)編程

「主頁鏈接」個(gè)人主頁

「筆者」楓葉先生(fy)

「楓葉先生有點(diǎn)文青病」「每篇一句」

滿堂花醉三千客,

一劍霜寒十四州。

——貫休《獻(xiàn)錢尚父》

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

目錄

四、Linux線程互斥

4.1?進(jìn)程線程間的互斥相關(guān)概念

4.2?互斥量mutex

4.3?互斥量接口函數(shù)

4.4?互斥量實(shí)現(xiàn)原理

五、可重入和線程安全

5.1?概念

5.2?常見的線程不安全的情況

5.3?常見的線程安全的情況

5.4?常見不可重入的情況

5.5 常見可重入的情況

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

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

六、死鎖

6.1 概念

6.2?死鎖四個(gè)必要條件

6.3?避免死鎖

七、Linux線程同步

7.1?同步概念與競(jìng)態(tài)條件

7.2 條件變量

7.3 條件變量相關(guān)函數(shù)


四、Linux線程互斥

4.1?進(jìn)程線程間的互斥相關(guān)概念

  • 臨界資源:多線程執(zhí)行流共享的資源就叫做臨界資源
  • 臨界區(qū):每個(gè)線程內(nèi)部,訪問臨界資源的代碼,就叫做臨界區(qū)
  • 互斥:任何時(shí)刻,互斥保證有且只有一個(gè)執(zhí)行流進(jìn)入臨界區(qū),訪問臨界資源,通常對(duì)臨界資源起保護(hù)作用
  • 原子性:不會(huì)被任何調(diào)度機(jī)制打斷的操作,該操作只有兩態(tài),要么完成,要么未完成

臨界資源&&臨界區(qū)

如何理解臨界資源和臨界區(qū)??

在前面進(jìn)程間通信,進(jìn)程間想要通信,必須得依賴第三方資源,因?yàn)檫M(jìn)程之間是互相獨(dú)立的,第三方資源比如是:管道、共享內(nèi)存等等。進(jìn)程間通信中的第三方資源就叫做臨界資源,訪問第三方資源的代碼就叫做臨界區(qū)。第三方資源也叫共享資源

而線程之間想要通信則比較簡(jiǎn)單,因?yàn)榫€程之間的大部分資源都是共享的。比如,定義一個(gè)全局變量 ticket,這個(gè)ticket就是一個(gè)共享資源,每個(gè)線程都可以看得到這份資源。

假設(shè)ticket是電影售票系統(tǒng)中的一種電影的票,ticket 一共有1000張,現(xiàn)在有兩個(gè)線程進(jìn)行對(duì)該電影票進(jìn)行搶票行為,代碼如下:

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;

// 票 -- 共享資源
int tickets = 1000;

void* getTicket(void* args)
{
    string username = static_cast<const char*>(args);
    while(1)
    {
        if(tickets > 0)
        {
            cout << username << ": 正在進(jìn)行搶票 "  << tickets-- << endl;
            sleep(1);//模擬搶票
        }
        else
        {
            break;
        }
    }
}

int main()
{
    pthread_t tid1, tid2;
    pthread_create(&tid1, nullptr, getTicket, (void*)"thread 1");
    pthread_create(&tid2, nullptr, getTicket, (void*)"thread 2");

    pthread_join(tid1, nullptr);
    pthread_join(tid2, nullptr);

    return 0;
}

編譯運(yùn)行,兩個(gè)線程都可以進(jìn)行搶票

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

在上面的例子中,全局變量 tickets 就是臨界資源,每個(gè)線程中對(duì)臨界資源進(jìn)行訪問的代碼稱為臨界區(qū)

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

原子性與互斥

在多線程情況下,如果這多個(gè)執(zhí)行流都自顧自的對(duì)臨界資源進(jìn)行操作,那么此時(shí)就可能導(dǎo)致數(shù)據(jù)不一致的問題,會(huì)產(chǎn)生線程安全的問題。

比如,多個(gè)線程進(jìn)行搶票,主線程創(chuàng)建5個(gè)線程進(jìn)行搶票,票數(shù)為0線程就自動(dòng)結(jié)束了,主線程只需 join 即可

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;

// 票 -- 共享資源
int tickets = 1000;

void* getTicket(void* args)
{
    string username = static_cast<const char*>(args);
    while(1)
    {
        if(tickets > 0)
        {
             //模擬搶票花費(fèi)的時(shí)間
            usleep(12345);//微秒
            cout << username << ": 正在進(jìn)行搶票 "  << tickets-- << endl;
        }
        else
        {
            break;
        }
    }
}

int main()
{
    pthread_t tid1, tid2, tid3, tid4, tid5;
    pthread_create(&tid1, nullptr, getTicket, (void*)"thread 1");
    pthread_create(&tid2, nullptr, getTicket, (void*)"thread 2");
    pthread_create(&tid3, nullptr, getTicket, (void*)"thread 3");
    pthread_create(&tid4, nullptr, getTicket, (void*)"thread 4");
    pthread_create(&tid5, nullptr, getTicket, (void*)"thread 5");

    pthread_join(tid1, nullptr);
    pthread_join(tid2, nullptr);
    pthread_join(tid3, nullptr);
    pthread_join(tid4, nullptr);
    pthread_join(tid5, nullptr);
    return 0;
}

編譯運(yùn)行,多運(yùn)行幾次,我們發(fā)現(xiàn)票數(shù)居然變成負(fù)數(shù)了。票數(shù)本來就1000張,你還賣出了1001、1002、1003、1004張,這明顯不合理,這就是多個(gè)線程同時(shí)訪問一塊資源,帶來的線程安全的問題

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

  • 上面的線程就是交叉執(zhí)行
  • 多個(gè)線程交叉執(zhí)行本質(zhì):就是讓調(diào)度器盡可能的頻繁發(fā)生線程調(diào)度與切換
  • 線程一般發(fā)生切換:時(shí)間片到了,來了更高優(yōu)先級(jí)的線程,線程等待的時(shí)候。
  • 線程是在什么時(shí)候檢測(cè)上面的問題呢?從內(nèi)核態(tài)返回用戶態(tài)的時(shí)候,線程要對(duì)調(diào)度狀態(tài)進(jìn)行檢測(cè),如果可以,就直接發(fā)生線程切換

上面代碼中 tickets 就是臨界資源,因?yàn)樗欢鄠€(gè)執(zhí)行流同時(shí)訪問,而判斷tickets是否大于0、打印剩余票數(shù)以及對(duì)票數(shù)進(jìn)行 --,這些代碼就是臨界區(qū),因?yàn)檫@些代碼對(duì)臨界資源進(jìn)行了訪問

剩余票數(shù)出現(xiàn)負(fù)數(shù)的原因:

  • 臨界區(qū)可以被多個(gè)線程進(jìn)行并發(fā)(同時(shí))訪問,臨界資源沒有受到保護(hù)
  • usleep 這個(gè)模擬漫長業(yè)務(wù)的過程,在這個(gè)漫長的業(yè)務(wù)過程中,可能有很多個(gè)線程會(huì)進(jìn)入該代碼段
  • tickets-- 操作不是原子性的

如何對(duì)臨界區(qū)進(jìn)行保護(hù)??

進(jìn)行互斥,互斥的作用就是,保證在任何時(shí)候有且只有一個(gè)執(zhí)行流(線程)進(jìn)入臨界區(qū),對(duì)臨界資源進(jìn)行訪問

為什么 tickets-- 操作不是原子性的??

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

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

  1. load :將共享變量 ticke t從內(nèi)存加載到寄存器中
  2. update : 更新寄存器里面的值,執(zhí)行-1操作
  3. store :將新值,從寄存器寫回共享變量 ticket 的內(nèi)存地址

如果是原子性的,就是要一步完成,沒有分出多步。即對(duì)一個(gè)資源進(jìn)行的操作,如果只用一條匯編就能完成,就為原子性

。作對(duì)應(yīng)的匯編代碼如下:

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

對(duì)應(yīng)操作如下圖,-- 操作分三步

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

分析為什么票數(shù)會(huì)變成負(fù)數(shù)?

假設(shè)線程1剛執(zhí)行完? if(tickets > 0) 的判斷,線程1就CPU被切走了,也就是從CPU上剝離下來,切走也要保存該線程的上文數(shù)據(jù),因?yàn)橐粋€(gè)CPU的寄存器只有一套,進(jìn)行線程切換必須要對(duì)該進(jìn)程的上下文數(shù)據(jù)進(jìn)行保存。假設(shè)此時(shí)票數(shù)還剩1張

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

這時(shí)來了一個(gè)線程2,線程2執(zhí)行完? if(tickets > 0)的判斷后,也執(zhí)行完了 -- 操作,即(1)從內(nèi)存讀取 tickets 到CPU的寄存器中,(2)在寄存器中進(jìn)行邏輯運(yùn)算,(3)重新把更新后的 tickets 寫回內(nèi)存

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

這時(shí),CPU又開始執(zhí)行線程1,重新把線程1的上下文加載到寄存器當(dāng)中,線程1繼續(xù)執(zhí)行原來的代碼,即準(zhǔn)備執(zhí)行打印、 -- 操作。線程1此時(shí)執(zhí)行 -- 操作時(shí),(1)從內(nèi)存讀取 tickets 到CPU的寄存器中,此時(shí)tickets為0。(2)在寄存器中進(jìn)行邏輯運(yùn)算,-1后tickets變成了-1(3)重新把更新后的 tickets 寫回內(nèi)存,此時(shí)tickets就變成了負(fù)數(shù)

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

這僅僅是其中的一種情況,如果是 -- 操作的三步其中第一步,線程1就被CPU切走了(這種情況模擬不出來,CPU太快了,只能進(jìn)行口述),假設(shè)票數(shù)是1000

-- 操作需要三個(gè)步驟才能完成,那么就有可能當(dāng)thread1剛把 tickets 的值讀進(jìn)CPU就被切走了,也就是從CPU上剝離下來,假設(shè)此時(shí)thread1讀取到的值就是1000,而當(dāng)thread1被切走時(shí),寄存器中的1000叫做thread1的上下文信息,因此需要被保存起來,之后thread1就被掛起了

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

假設(shè)此時(shí)thread2被調(diào)度了,由于thread1只進(jìn)行了--操作的第一步,因此thread2此時(shí)看到tickets的值還是1000,而系統(tǒng)給thread2的時(shí)間片可能較多,導(dǎo)致thread2 一次性執(zhí)行了500次 -- 才被切走,最終tickets由1000減到了500

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

此時(shí)CPU再把thread1恢復(fù)上來,恢復(fù)的本質(zhì)就是繼續(xù)執(zhí)行thread1的代碼,并且要將thread1曾經(jīng)的上下文信息恢復(fù)出來,此時(shí)寄存器當(dāng)中的值是恢復(fù)出來的1000,然后thread1繼續(xù)執(zhí)行?--操作的第二步和第三步,最終將999寫回內(nèi)存,這簡(jiǎn)直極天理難容

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

此時(shí)就可能導(dǎo)致數(shù)據(jù)不一致的問題,發(fā)生了數(shù)據(jù)安全性的問題,這就是多線程產(chǎn)生線程安全的問題。

因此對(duì)一個(gè)變量進(jìn)行--操作并不是原子的,雖然tickets--就是一行代碼,但這行代碼被編譯器編譯后本質(zhì)上是三行匯編,對(duì)應(yīng) ++操作也不是原子的

如何解決這些問題??互斥量(互斥鎖)

4.2?互斥量mutex

  • 大部分情況,線程使用的數(shù)據(jù)都是局部變量,變量的地址空間在線程獨(dú)立??臻g內(nèi),這種情況的變量歸屬單個(gè)線程
  • 但有時(shí)候,很多變量都需要在線程間共享,這樣的變量稱為共享變量,可以通過數(shù)據(jù)的共享,完成線程之間的交互。
  • 多個(gè)線程并發(fā)的操作共享變量,會(huì)帶來一些線程安全的問題,比如上面舉例的

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

  1. 代碼必須要有互斥行為:當(dāng)代碼進(jìn)入臨界區(qū)執(zhí)行時(shí),不允許其他線程進(jìn)入該臨界區(qū)。
  2. 如果多個(gè)線程同時(shí)要求執(zhí)行臨界區(qū)的代碼,并且臨界區(qū)沒有線程在執(zhí)行,那么只能允許一個(gè)線程進(jìn)入該臨界區(qū)。
  3. 如果線程不在臨界區(qū)中執(zhí)行,那么該線程不能阻止其他線程進(jìn)入臨界區(qū)

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

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

4.3?互斥量接口函數(shù)

初始化互斥量

互斥量是需要初始化才能使用的,初初始化互斥量有兩種方法:靜態(tài)分配和動(dòng)態(tài)分配

(1)靜態(tài)分配

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

(2)動(dòng)態(tài)分配

互斥量初始化的函數(shù)是?pthread_mutex_init,man 3?pthread_mutex_init 查看:

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

函數(shù):pthread_mutex_init

頭文件: #include <pthread.h>

函數(shù)原型:
        int pthread_mutex_init(pthread_mutex_t *restrict mutex,
              const pthread_mutexattr_t *restrict attr);

參數(shù):
    第一個(gè)參數(shù)mutex:需要初始化的互斥量
    第二個(gè)參數(shù)attr:初始化互斥量的屬性,一般設(shè)置為空即可

返回值:
    互斥量初始化成功返回0,失敗返回錯(cuò)誤碼

?銷毀互斥量

互斥量使用完了需要進(jìn)行銷毀,互斥量銷毀函數(shù)是?pthread_mutex_destroy?

函數(shù): pthread_mutex_destroy

頭文件:#include <pthread.h>

函數(shù)原型:
        int pthread_mutex_destroy(pthread_mutex_t *mutex);

參數(shù):
    mutex:需要銷毀的互斥量

返回值:
     互斥量銷毀成功返回0,失敗返回錯(cuò)誤碼

銷毀互斥量需要注意:

  • 使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要銷毀
  • 不要銷毀一個(gè)已經(jīng)加鎖的互斥量
  • 已經(jīng)銷毀的互斥量,要確保后面不會(huì)有線程再嘗試加鎖

互斥量加鎖

互斥量加鎖的函數(shù)叫做pthread_mutex_lock,man 3 查看:

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

函數(shù):pthread_mutex_lock

頭文件:#include <pthread.h>

函數(shù)原型:
        int pthread_mutex_lock(pthread_mutex_t *mutex);

參數(shù):
    mutex:需要加鎖的互斥量

返回值:
    互斥量加鎖成功返回0,失敗返回錯(cuò)誤碼

?互斥量解鎖

互斥量解鎖的函數(shù)叫做?pthread_mutex_unlock?

函數(shù):pthread_mutex_unlock

頭文件:#include <pthread.h>

函數(shù)原型:
        int pthread_mutex_unlock(pthread_mutex_t *mutex);

參數(shù):
    mutex:需要解鎖的互斥量

返回值:
    互斥量解鎖成功返回0,失敗返回錯(cuò)誤碼

注意,使用pthread_mutex_lock,可能會(huì)遇到以下情況:

  • 互斥量處于未鎖狀態(tài),該函數(shù)會(huì)將互斥量鎖定,同時(shí)返回成功
  • 發(fā)起函數(shù)調(diào)用時(shí),其他線程已經(jīng)鎖定互斥量,或者存在其他線程同時(shí)申請(qǐng)互斥量,但沒有競(jìng)爭(zhēng)到互斥量,那么?pthread_mutex_lock?調(diào)用會(huì)陷入阻塞(執(zhí)行流被掛起),等待互斥量解鎖

改進(jìn)上面 4.1 的例子

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;

//定義互斥量,全局,每個(gè)線程都可以看到
pthread_mutex_t mutex;

// 票 -- 共享資源
int tickets = 1000;

void* getTicket(void* args)
{
    string username = static_cast<const char*>(args);
    while(1)
    {
        pthread_mutex_lock(&mutex);//加鎖
        if(tickets > 0)
        {
             //模擬搶票花費(fèi)的時(shí)間
            usleep(12345);//微秒
            cout << username << ": 正在進(jìn)行搶票 "  << tickets-- << endl;
            pthread_mutex_unlock(&mutex);//解鎖
        }
        else{
            pthread_mutex_unlock(&mutex);//解鎖
            break;
        }
    }
}

int main()
{
    pthread_mutex_init(&mutex, nullptr);//初始化互斥量
    pthread_t tid1, tid2, tid3, tid4, tid5;
    pthread_create(&tid1, nullptr, getTicket, (void*)"thread 1");
    pthread_create(&tid2, nullptr, getTicket, (void*)"thread 2");
    pthread_create(&tid3, nullptr, getTicket, (void*)"thread 3");
    pthread_create(&tid4, nullptr, getTicket, (void*)"thread 4");
    pthread_create(&tid5, nullptr, getTicket, (void*)"thread 5");

    pthread_join(tid1, nullptr);
    pthread_join(tid2, nullptr);
    pthread_join(tid3, nullptr);
    pthread_join(tid4, nullptr);
    pthread_join(tid5, nullptr);
    pthread_mutex_destroy(&mutex);//使用完了,銷毀互斥量
    return 0;
}

臨界區(qū):

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

?編譯運(yùn)行,不會(huì)再出現(xiàn)負(fù)數(shù)

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

我們從運(yùn)行的過程來看:

  • 序執(zhí)行變慢了,應(yīng)為有了互斥量,加鎖到解鎖的過程,多個(gè)線程是串行的(同一時(shí)間只能有一個(gè)線程執(zhí)行)
  • 但是我們也發(fā)現(xiàn),這些票只有一個(gè)線程在搶(后面需要用線程同步解決
  • 互斥鎖只規(guī)定互斥訪問,沒有規(guī)定必須讓誰先申請(qǐng),誰獲得鎖是多個(gè)執(zhí)行流競(jìng)爭(zhēng)的結(jié)果

搶票后去做一些其他的事:比如生成訂單

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

編譯運(yùn)行,不會(huì)出現(xiàn)只有一個(gè)線程搶票的情況(后面需要用線程同步解決

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

4.4?互斥量實(shí)現(xiàn)原理

如何看待互斥鎖?

  • 全局的變量是臨界資源,是需要要被保護(hù)的,鎖是用來保護(hù)臨界資源的
  • 鎖是一個(gè)全局資源,所以鎖本身也是一個(gè)臨界資源,鎖需要被保護(hù)么?既然鎖是臨界資源,那么鎖就必須被保護(hù)起來,但鎖本身就是用來保護(hù)臨界資源的,那鎖又由誰來保護(hù)的呢?
  • 鎖實(shí)際上是自己保護(hù)自己的,我們只需要保證申請(qǐng)鎖的過程是原子的,那么鎖就是安全的。
  • pthread_mutex_lock、pthread_mutex_unlock:加鎖和解鎖的過程其實(shí)就是原子的:如果申請(qǐng)成功,就繼續(xù)向后執(zhí)行,如果申請(qǐng)暫時(shí)沒有成功,執(zhí)行流會(huì)阻塞,誰持有鎖,誰進(jìn)入臨界區(qū)。

在臨界區(qū)內(nèi)執(zhí)行的線程會(huì)進(jìn)行線程切換嗎?

  • 臨界區(qū)內(nèi)的線程完全可以進(jìn)行線程切換,但即便該線程被切走,其他線程也無法進(jìn)入臨界區(qū)進(jìn)行資源訪問
  • 因?yàn)榇藭r(shí)該線程是拿著鎖被切走的,鎖沒有被釋放也就意味著其他線程無法申請(qǐng)到鎖,也就無法進(jìn)入臨界區(qū)進(jìn)行資源訪問了。
  • 其他想進(jìn)入該臨界區(qū)進(jìn)行資源訪問的線程,必須等該線程執(zhí)行完臨界區(qū)的代碼并釋放鎖之后,才能申請(qǐng)鎖,申請(qǐng)到鎖之后才能進(jìn)入臨界區(qū)。

互斥鎖的原子性如何體現(xiàn)?

對(duì)于其他線程而言,有意義的鎖的形態(tài)只有兩種:(1)申請(qǐng)鎖前,(2)申請(qǐng)鎖后。站在其他線程的角度,看待當(dāng)前線程持有鎖的過程,就是原子的?

加鎖、解鎖過程如何保證原子性?? (互斥鎖的原理)

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

加鎖和解鎖的偽代碼如下:

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

%al 是CPU內(nèi)的一個(gè)寄存器(不同體系結(jié)構(gòu)叫法不一樣)?,lock:加鎖(申請(qǐng))的匯編代碼,unlock:解鎖的匯編代碼,都是偽代碼,方便理解,假設(shè)mutex的初始值是1

申請(qǐng)鎖的過程:

  1. 先把0移動(dòng)到 %al 寄存器里面,即清0
  2. mutex 變量是我們定義的一個(gè)互斥鎖
  3. xchgb %al, mutex 就是交換 %al 寄存器和 mutex 中的值,該指令可以完成寄存器和內(nèi)存單元之間數(shù)據(jù)的交換,mutex是存在于內(nèi)存中的(該交換是一條匯編語句完成)
  4. 然后判斷寄存器內(nèi)的內(nèi)容是否大于0,大于0申請(qǐng)鎖成功,此時(shí)就可以進(jìn)入臨界區(qū)訪問對(duì)應(yīng)的臨界資源。
  5. 寄存器內(nèi)的內(nèi)容是不大于0,申請(qǐng)失敗,線程被掛起進(jìn)行等待,直到鎖被釋放后再次競(jìng)爭(zhēng)申請(qǐng)鎖

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

釋放鎖的過程:

  1. 將內(nèi)存中的mutex的值移動(dòng)為1
  2. 然后喚醒被掛起的線程,即在等待mutex的線程

申請(qǐng)鎖和釋放鎖的過程不怕被CPU切走,切走回來時(shí)恢復(fù)上下文數(shù)據(jù)即可,所以加鎖和解鎖操作通常是線程安全的

五、可重入和線程安全

5.1?概念

  • 線程安全:多個(gè)線程并發(fā)同一段代碼時(shí),不會(huì)出現(xiàn)不同的結(jié)果。常見對(duì)全局變量或者靜態(tài)變量進(jìn)行操作,并且沒有鎖保護(hù)的情況下,會(huì)出現(xiàn)該問題。
  • 重入:同一個(gè)函數(shù)被不同的執(zhí)行流調(diào)用,當(dāng)前一個(gè)流程還沒有執(zhí)行完,就有其他的執(zhí)行流再次進(jìn)入,我們稱之為重入。一個(gè)函數(shù)在重入的情況下,運(yùn)行結(jié)果不會(huì)出現(xiàn)任何不同或者任何問題,則該函數(shù)被稱為可重入函數(shù),否則,是不可重入函數(shù)

5.2?常見的線程不安全的情況

  • 不保護(hù)共享變量的函數(shù)
  • 函數(shù)狀態(tài)隨著被調(diào)用,狀態(tài)發(fā)生變化的函數(shù)
  • 返回指向靜態(tài)變量指針的函數(shù)
  • 調(diào)用線程不安全函數(shù)的函數(shù)

5.3?常見的線程安全的情況

  • 每個(gè)線程對(duì)全局變量或者靜態(tài)變量只有讀取的權(quán)限,而沒有寫入的權(quán)限,一般來說這些線程是安全的
  • 類或者接口對(duì)于線程來說都是原子操作
  • 多個(gè)線程之間的切換不會(huì)導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性

5.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)

5.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ù)

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

  • 函數(shù)是可重入的,那就是線程安全的
  • 函數(shù)是不可重入的,那就不能由多個(gè)線程使用,有可能引發(fā)線程安全問題
  • 如果一個(gè)函數(shù)中有全局變量,那么這個(gè)函數(shù)既不是線程安全也不是可重入的

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

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

六、死鎖

6.1 概念

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

  • 在未來,我們可能會(huì)使用多把鎖,假設(shè)線程A持有自己的鎖不釋放,而且還有對(duì)方的鎖,線程B也是如此,線程CDE...,此時(shí)就容易造成死鎖
  • 單執(zhí)行流也有可能產(chǎn)生死鎖,如果某一執(zhí)行流連續(xù)申請(qǐng)了兩次鎖(寫的代碼有問題),那么此時(shí)該執(zhí)行流就會(huì)被掛起。因?yàn)樵搱?zhí)行流第一次申請(qǐng)鎖的時(shí)候是申請(qǐng)成功的,但第二次申請(qǐng)鎖時(shí)因?yàn)樵撴i已經(jīng)被申請(qǐng)過了,于是申請(qǐng)失敗導(dǎo)致被掛起直到該鎖被釋放時(shí)才會(huì)被喚醒,但是這個(gè)鎖本來就在自己手上,自己現(xiàn)在處于被掛起的狀態(tài)根本沒有機(jī)會(huì)釋放鎖,所以該執(zhí)行流將永遠(yuǎn)不會(huì)被喚醒,此時(shí)該執(zhí)行流也就處于一種死鎖的狀態(tài)

6.2?死鎖四個(gè)必要條件

  1. 互斥條件:一個(gè)資源每次只能被一個(gè)執(zhí)行流使用
  2. 請(qǐng)求與保持條件:一個(gè)執(zhí)行流因請(qǐng)求資源而阻塞時(shí),對(duì)已獲得的資源保持不放
  3. 不剝奪條件:一個(gè)執(zhí)行流已獲得的資源,在末使用完之前,不能強(qiáng)行剝奪
  4. 循環(huán)等待條件:若干執(zhí)行流之間形成一種頭尾相接的循環(huán)等待資源的關(guān)系

注:同時(shí)滿足了這四個(gè)條件才會(huì)產(chǎn)生死鎖

6.3?避免死鎖

  • 破壞死鎖的四個(gè)必要條件之中的任何一個(gè)條件
  • 加鎖順序一致
  • 避免鎖未釋放的場(chǎng)景
  • 資源一次性分配

除此之外,還有一些避免死鎖的算法,比如死鎖檢測(cè)算法和銀行家算法。

七、Linux線程同步

7.1?同步概念與競(jìng)態(tài)條件

  • 同步:在保證數(shù)據(jù)安全的前提下,讓線程能夠按照某種特定的順序訪問臨界資源,從而有效避免饑餓問題,叫做同步
  • 競(jìng)態(tài)條件:因?yàn)闀r(shí)序問題,而導(dǎo)致程序異常,稱之為競(jìng)態(tài)條件

同步解釋如下:?

  • 如果只是單純的加鎖,是會(huì)存在一些問題的,假設(shè)某個(gè)線程的競(jìng)爭(zhēng)力特別強(qiáng),每次都能夠申請(qǐng)到鎖,但是申請(qǐng)到鎖之后什么也不做,所以在我們看來這個(gè)線程就一直在申請(qǐng)鎖和釋放鎖,這就可能導(dǎo)致其他線程長時(shí)間競(jìng)爭(zhēng)不到鎖,會(huì)引起饑餓問題。
  • 單純的加鎖是沒有錯(cuò)的,它能夠保證在同一時(shí)間只有一個(gè)線程進(jìn)入臨界區(qū),但是它不合理,它沒有高效的讓每一個(gè)線程使用這份臨界資源。
  • 現(xiàn)在增加一個(gè)規(guī)則,當(dāng)一個(gè)線程釋放鎖后,這個(gè)線程不能立馬再次申請(qǐng)鎖,該線程必須排到這個(gè)鎖的資源等待隊(duì)列的最后進(jìn)行排隊(duì)。
  • 增加這個(gè)規(guī)則之后,下一個(gè)獲取到鎖的資源的線程就一定是在資源等待隊(duì)列首部的線程,這就是線程同步

為了支撐線程同步,需要用到條件變量

7.2 條件變量

條件變量的概念

條件變量是一種同步機(jī)制,用于在多個(gè)線程之間進(jìn)行通信。它允許一個(gè)線程等待另一個(gè)線程滿足特定的條件,然后再繼續(xù)執(zhí)行。條件變量通常與互斥鎖一起使用,以確保線程安全。條件變量提供了一種高效的方式來實(shí)現(xiàn)線程之間的同步和通信。

一個(gè)例子幫助理解條件變量:

  • 假設(shè)有一間房間是面試的地方,里面有一位面試官在面試,公司給參見面試的同學(xué)發(fā)送了面試通知,然后一大堆的同學(xué)到這個(gè)面試房間的面前等待。當(dāng)一名同學(xué)面試完成了,面試官準(zhǔn)備面試下一位同學(xué)的時(shí)候,面試官發(fā)現(xiàn)門口都站滿了等待面試的同學(xué)。
  • 面試官不知道輪到哪個(gè)了,就隨便叫了離他最近的一名同學(xué)
  • 假如那個(gè)叫進(jìn)去的同學(xué)是來得比較晚的,他的并不是前面來的同學(xué),就因?yàn)樗x面試官近,就先進(jìn)去了,這符合規(guī)則么?符合,但是這不合理
  • 后面,來個(gè)一個(gè)管理者,對(duì)面試的同學(xué)進(jìn)行管理,管理者直接立起一塊牌子:要面試就先要排隊(duì),只會(huì)從排隊(duì)的里面按順序進(jìn)行面試,不排隊(duì)不能進(jìn)行面試
  • 立起來的這個(gè)牌子就相當(dāng)于條件變量,只有符合條件,才允許你進(jìn)行面試。
  • 面試的同學(xué)就相當(dāng)于一個(gè)個(gè)的線程,這個(gè)面試的房間就相當(dāng)于一個(gè)公共的資源,即臨界資源,所有進(jìn)程都想訪問這個(gè)資源
  • 線程想訪問這個(gè)臨界資源,線程就必須要滿足條件變量,否則線程只能去條件變量下等待

轉(zhuǎn)換成以下可以是:

  1. 條件變量的內(nèi)部自帶 "排隊(duì)" 的隊(duì)列
  2. 誰調(diào)用條件變量的等待函數(shù),誰就去排隊(duì)
  3. 當(dāng)一個(gè)線程收到 “進(jìn)入面試房間的信號(hào)”,它就會(huì)被從隊(duì)列的頭部拿出,允許它訪問使用臨界資源 “面試房間”

7.3 條件變量相關(guān)函數(shù)

初始化條件變量

條件變量跟互斥量一樣是需要初始化的,初始化的函數(shù)是pthread_cond_init,man 3?pthread_cond_init 查看:

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

函數(shù):pthread_cond_init

頭文件:#include <pthread.h>

函數(shù)原型:
         int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);

參數(shù):
    第一個(gè)參數(shù)cond:需要初始化的條件變量
    第二個(gè)參數(shù)attr:初始化條件變量的屬性,一般設(shè)置為空即可

返回值:
    條件變量初始化成功返回0,失敗返回錯(cuò)誤碼

?調(diào)用pthread_cond_init函數(shù)初始化條件變量叫做動(dòng)態(tài)分配,除此之外,我們還可以用下面這種方式初始化條件變量,該方式叫做靜態(tài)分配:

 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

銷毀條件變量

銷毀條件變量的函數(shù)叫做pthread_cond_destroy

函數(shù):pthread_cond_destroy

頭文件:#include <pthread.h>

函數(shù)原型:
        int pthread_cond_destroy(pthread_cond_t *cond);

參數(shù):
    cond:需要銷毀的條件變量

返回值:
    條件變量初始化成功返回0,失敗返回錯(cuò)誤碼

注意:使用?PTHREAD_COND_INITIALIZER初始化的條件變量不需要銷毀

等待條件變量滿足

等待條件變量滿足的函數(shù)叫做pthread_cond_wait?,man 3?pthread_cond_wait 查看:

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

函數(shù):pthread_cond_wait

頭文件:#include <pthread.h>

函數(shù)原型:
        int pthread_cond_wait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex);

參數(shù):
    第一個(gè)參數(shù)cond:需要等待的條件變量。
    第二個(gè)參數(shù)mutex:當(dāng)前線程所處臨界區(qū)對(duì)應(yīng)的互斥鎖

返回值:
    條件變量初始化成功返回0,失敗返回錯(cuò)誤碼

喚醒等待

喚醒等待的函數(shù)有兩個(gè)?pthread_cond_signal 和?pthread_cond_broadcast ,man 3 查看:

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

函數(shù):pthread_cond_broadcast 和 pthread_cond_signal

頭文件:#include <pthread.h>

函數(shù)原型:
        int pthread_cond_broadcast(pthread_cond_t *cond);
        int pthread_cond_signal(pthread_cond_t *cond);

參數(shù):
    cond:?jiǎn)拘言赾ond條件變量下等待的線程

返回值:
    條件變量初始化成功返回0,失敗返回錯(cuò)誤碼

區(qū)別:

  • pthread_cond_signal函數(shù)用于喚醒等待隊(duì)列中首個(gè)線程
  • pthread_cond_broadcast函數(shù)用于喚醒等待隊(duì)列中的全部線程

使用條件變量的流程:

  1. 當(dāng)一個(gè)線程需要等待某個(gè)條件時(shí),它會(huì)調(diào)用條件變量的等待函數(shù),并釋放互斥鎖。
  2. 當(dāng)另一個(gè)線程滿足條件時(shí),它會(huì)發(fā)送信號(hào)通知等待線程,并重新獲取互斥鎖。
  3. 等待線程接收到信號(hào)后,會(huì)重新獲取互斥鎖并檢查條件是否滿足,如果滿足就繼續(xù)執(zhí)行,否則繼續(xù)等待。

例子,還是上面搶票的例子

主線程創(chuàng)建4個(gè)新線程,讓主線程控制這4個(gè)新線程,這4個(gè)新線程創(chuàng)建后都在條件變量下進(jìn)行等待,直到主線程喚醒一個(gè)等待線程,線程才會(huì)執(zhí)行

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;

// 票 -- 共享資源
int tickets = 1000;

//定義全局互斥量 -- 每個(gè)線程都可以看到
pthread_mutex_t mutex;
//定義全局的條件變量
pthread_cond_t cond;

void* getTicket(void* args)
{
    string username = static_cast<const char*>(args);
    while(1)
    {
        pthread_mutex_lock(&mutex);//加鎖
        pthread_cond_wait(&cond, &mutex);//線程阻塞在這里,直到被喚醒
        if(tickets > 0)
        {
            //注意這里沒有進(jìn)行sleep
            cout << username << ": 正在進(jìn)行搶票 "  << tickets-- << endl;
            pthread_mutex_unlock(&mutex);//解鎖
        }
        else{
            pthread_mutex_unlock(&mutex);//解鎖
            break;
        }
    }
}

int main()
{
    pthread_mutex_init(&mutex, nullptr);//初始化互斥量
    pthread_cond_init(&cond, nullptr);//初始化條件變量

    pthread_t tid1, tid2, tid3, tid4, tid5;
    pthread_create(&tid1, nullptr, getTicket, (void*)"thread 1");
    pthread_create(&tid2, nullptr, getTicket, (void*)"thread 2");
    pthread_create(&tid3, nullptr, getTicket, (void*)"thread 3");
    pthread_create(&tid4, nullptr, getTicket, (void*)"thread 4");

    while(1)
    {
        pthread_cond_signal(&cond);//間隔一秒發(fā)送信號(hào),喚醒等待的線程(一個(gè))
        cout << "main thread wakeup one thread..." << endl;
        sleep(1);
    }

    pthread_join(tid1, nullptr);
    pthread_join(tid2, nullptr);
    pthread_join(tid3, nullptr);
    pthread_join(tid4, nullptr);

    pthread_mutex_destroy(&mutex);//使用完了,銷毀互斥量
    pthread_cond_destroy(&cond);//銷毀條件變量
    return 0;
}

編譯運(yùn)行

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

觀察現(xiàn)象會(huì)發(fā)現(xiàn)喚醒這四個(gè)線程時(shí)具有明顯的順序性,根本原因是當(dāng)這若干個(gè)線程啟動(dòng)時(shí)默認(rèn)都會(huì)在該條件變量下去等待,而我們每次都喚醒的是在當(dāng)前條件變量下等待的頭部線程,當(dāng)該線程執(zhí)行完打印操作后會(huì)繼續(xù)排到等待隊(duì)列的尾部進(jìn)行wait,所以我們能夠看到一個(gè)周轉(zhuǎn)的現(xiàn)象

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

如果我們想每次喚醒都將在該條件變量下等待的所有線程進(jìn)行喚醒,可以將代碼中的pthread_cond_signal函數(shù)改為pthread_cond_broadcast函數(shù)

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

編譯運(yùn)行,每一次喚醒都會(huì)喚醒在該條件變量下等待的所有線程,也就是每次都將這個(gè)線程喚醒

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

為什么 pthread_cond_wait 的第二個(gè)參數(shù)需要傳入互斥量??

  • 因?yàn)樵谡{(diào)用 pthread_cond_wait 函數(shù)時(shí),需要先將互斥量上鎖(加鎖在等待條件變量之前),然后將該互斥量傳遞給函數(shù)作為參數(shù),接著在函數(shù)內(nèi)部會(huì)將該互斥量解鎖并等待條件變量的信號(hào)。
  • 如果不傳遞互斥量,就無法保證在等待條件變量時(shí)對(duì)共享資源的訪問是互斥的,可能會(huì)導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)等問題,從而可能導(dǎo)致程序錯(cuò)誤或死鎖。
  • 因此,為了保證線程之間的安全操作,需要在調(diào)用 pthread_cond_wait 函數(shù)時(shí)傳遞互斥量。

線程互斥與同步完結(jié),下一篇進(jìn)入生產(chǎn)消費(fèi)者模型

--------------------- END ----------------------文章來源地址http://www.zghlxwxcb.cn/news/detail-432897.html

「 作者 」 楓葉先生
「 更新 」 2023.5.3
「 聲明 」 余之才疏學(xué)淺,故所撰文疏漏難免,
          或有謬誤或不準(zhǔn)確之處,敬請(qǐng)讀者批評(píng)指正。

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

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

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

相關(guān)文章

  • Linux——線程3|線程互斥和同步

    Linux——線程3|線程互斥和同步

    我們上一篇提到過,多個(gè)線程執(zhí)行下面代碼可能會(huì)出錯(cuò),具體原因可查看上一篇Linux博客。 為避免這種錯(cuò)誤的出現(xiàn),我們可采用加鎖保護(hù)。 PTHREAD_MUTEX_INITIALIZER 用pthread_mutex_t定義一把鎖。ptherad_mutex_init是對(duì)鎖進(jìn)行初始化的函數(shù)。如果這把鎖是全局的并且是靜態(tài)定義的,我們可

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

    【Linux】線程同步和互斥

    1.臨界資源:多線程執(zhí)行流共享的資源,且一次只能允許一個(gè)執(zhí)行流訪問的資源就叫做臨界資源。(多線程、多進(jìn)程打印數(shù)據(jù)) 2.臨界區(qū):每個(gè)線程內(nèi)部,訪問臨界資源的代碼,就叫做臨界區(qū)。 3.互斥:任何時(shí)刻,互斥保證有且只有一個(gè)執(zhí)行流進(jìn)入臨界區(qū),訪問臨界資源,通常對(duì)

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

    【Linux】Linux線程互斥與同步

    臨界資源:多線程執(zhí)行流共享的資源就叫做臨界資源 臨界區(qū):每個(gè)線程內(nèi)部,訪問臨界資源的代碼,就叫做臨界區(qū) 互斥:任何時(shí)刻,互斥保證有且只有一個(gè)執(zhí)行流進(jìn)入臨界區(qū),訪問臨界資源,通常對(duì)臨界資源起保護(hù)作用 原子性:不會(huì)被任何調(diào)度機(jī)制打斷的操作,該操作只有

    2024年02月04日
    瀏覽(22)
  • Linux-線程的同步與互斥

    Linux-線程的同步與互斥

    ?? 臨界資源:多線程指行流共享的資源叫做臨界資源。 ?? 臨界區(qū):每個(gè)線程內(nèi)部訪問臨界資源的代碼片段叫做臨界區(qū)。 ?? 互斥:任何時(shí)刻,互斥保證只有一個(gè)指行流進(jìn)入臨界區(qū),訪問臨界資源,通常是對(duì)臨界區(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.死鎖四個(gè)必

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

    Linux——線程的同步與互斥

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

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

    【Linux】多線程互斥與同步

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

    2024年02月09日
    瀏覽(16)
  • 【Linux】多線程2——線程互斥與同步/多線程應(yīng)用

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

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

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

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

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

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

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

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

    2024年02月06日
    瀏覽(21)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包