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

【Linux】多線程互斥與同步

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


一、線程互斥

1. 線程互斥的引出

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

#include <iostream>
#include <cstring>
#include <cassert>
#include <pthread.h>
#include <unistd.h>
#include "lockGuard.hpp"
#include "Thread.hpp"
using namespace std;
int tickets = 1000; // 加鎖保證共享資源的安全性

void* threadRoutine(void* args)
{
    string name = static_cast<const char*>(args);
    while(true)
    {
        if(tickets > 0)
        {
            usleep(2000); // 模擬搶票花費的時間
            cout << name << " get a ticket: " << tickets-- << endl;
        }
        else
        {
            break;
        }
        usleep(1000);
    }
    return nullptr;
}

int main()
{
    // 創(chuàng)建四個線程
    pthread_t tids[4];
    int n = sizeof(tids) / sizeof(tids[0]);
    for(int i = 0; i < n; i++)
    {
        char* data = new char[64];
        snprintf(data, 64, "thread-%d", i + 1);
        pthread_create(tids + i, nullptr, threadRoutine, data);
    }

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

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

這里我們可以看到,當(dāng)全局變量tickets被幾個執(zhí)行流共享時,最后變成了-1,這是因為如果我們?nèi)绻褂枚嗑€程對一個全局變量修改時,線程之間會相互影響,導(dǎo)致線程安全問題。

下面我們來看一下當(dāng)多個線程對共享變量進(jìn)行修改時,為什么會發(fā)生上述的線程安全問題?

假設(shè)有一個全局變量 g_val=100被兩個線程,線程A 和 線程B共享,在多線程環(huán)境下分別對同一個全局變量g_val進(jìn)行操作。

當(dāng)對變量進(jìn)行操作時會分為三個步驟:

  1. CPU把內(nèi)存中的數(shù)據(jù)讀到寄存器里
  2. 在寄存器中對數(shù)據(jù)進(jìn)行計算
  3. 將修改后的數(shù)據(jù)從寄存器里寫回內(nèi)存

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

下面我們來看一下線程A和線程B對全局變量進(jìn)行操作時的過程:

  1. 線程A執(zhí)行g(shù)_val- -操作
    【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器
    當(dāng)線程A執(zhí)行完第二步時,正準(zhǔn)備執(zhí)行第三步時,時間片到了,線程A需要將自己的上下文和數(shù)據(jù)帶走。
    此時的線程A認(rèn)為自己已經(jīng)將數(shù)據(jù)修改99了,當(dāng)下一次執(zhí)行時繼續(xù)執(zhí)行步驟三。

  2. 線程B在while中執(zhí)行g(shù)_val- -操作
    【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

線程B通過while循環(huán)了90次將g_val修改成了10,此時時間片到了。因此線程B也將自己的上下文保存了起來。

  1. 繼續(xù)執(zhí)行線程A
    【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

由于上次執(zhí)行線程A時第3步?jīng)]有執(zhí)行,所以線程A繼續(xù)執(zhí)行第3步。但是內(nèi)存中的g_val為上次線程B修改后的值10,所以線程A又將內(nèi)存中的值改成了99。

因此,一切的原因都是修改全局變量時線程調(diào)度切換、并發(fā)訪問進(jìn)而導(dǎo)致了數(shù)據(jù)不一致;想要解決這個問題,我們就需要進(jìn)行加鎖保護(hù)。


2. 互斥量

要解決以上問題,需要做到三點:

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

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

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

?? 初始化互斥量

  1. 靜態(tài)分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
  1. 動態(tài)分配
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
  • 參數(shù):
    mutex:要初始化的互斥量
    attr:NULL

?? 互斥量加鎖和解鎖

// 加鎖
int pthread_mutex_lock(pthread_mutex_t *mutex);
// 解鎖
int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 返回值:成功返回0,失敗返回錯誤號

?? 銷毀互斥量

int pthread_mutex_destroy(pthread_mutex_t *mutex);

注意:

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

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

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

?? 下面我們來使用互斥鎖來改進(jìn)一下改進(jìn)上面的售票系統(tǒng):

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

int tickets = 1000; // 加鎖保證共享資源的安全性
pthread_mutex_t mutex; // 定義一把鎖

void* threadRoutine(void* args)
{
    string name = static_cast<const char*>(args);
    while(true)
    {
        pthread_mutex_lock(&mutex);
        if(tickets > 0)
        {
            usleep(2000); // 模擬搶票花費的時間
            cout << name << " get a ticket: " << tickets-- << endl;
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
        usleep(1000);
    }
    return nullptr;
}

int main()
{
    pthread_mutex_init(&mutex, nullptr); // 初始化鎖
    // 創(chuàng)建四個線程
    pthread_t tids[4];
    int n = sizeof(tids) / sizeof(tids[0]);
    for(int i = 0; i < n; i++)
    {
        char* data = new char[64];
        snprintf(data, 64, "thread-%d", i + 1);
        pthread_create(tids + i, nullptr, threadRoutine, data);
    }
    for(int i = 0; i < 4; i++)
    {
        pthread_join(tids[i], nullptr);
    }
    pthread_mutex_destroy(&mutex);
    return 0;
}

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

因為加鎖會導(dǎo)致臨界區(qū)代碼串行訪問(互斥),從而導(dǎo)致代碼的執(zhí)行效率減低,因此我們在加鎖之后會發(fā)現(xiàn)代碼的運行速度比不加鎖之前慢了許多。因此,進(jìn)行加鎖訪問時,保證加鎖的粒度越小越好,不要將不訪問臨界區(qū)資源的代碼加鎖。


3. 互斥鎖的實現(xiàn)原理

互斥鎖的進(jìn)一步認(rèn)識:

  • 加了鎖之后,線程在臨界區(qū)中也會被切換,但這樣也不會有問題。因為線程是帶著鎖進(jìn)行線程切換的,其余線程是無法申請到鎖的,無法進(jìn)入臨界區(qū)訪問臨界資源。
  • 錯誤的編碼方式:線程不申請鎖直接訪問臨界區(qū)資源,這樣的話,就算別的線程持有鎖,該線程也可以進(jìn)入到臨界區(qū)。
  • 在沒有持有鎖的線程看來,對該線程最有意義的情況只用兩種:
    1. 線程 1 沒有持有鎖(什么都沒做)
    2. 線程 1 釋放鎖(做完),此時我可以申請鎖。那么在線程 1 持有鎖的期間,所做的所有操作在其他線程看來都是原子的!
  • 加鎖后,執(zhí)行臨界區(qū)的代碼一定是串行執(zhí)行的!
  • 要訪問臨界資源,每一個線程都必須先申請鎖,那么每一個線程都必須先看到同一把鎖并訪問它,所以鎖本身也是一種共享資源。那么鎖肯定也要保護(hù)起來,為了保護(hù)鎖的安全,申請和釋放鎖的操作都必須是原子的!

互斥鎖的細(xì)節(jié):

  1. 凡是訪問同一個臨界資源的線程,都要進(jìn)行加鎖保護(hù),而且必須加同一把鎖,這個是一個游戲規(guī)則,不能有例外。
  2. 每一個線程訪問臨界區(qū)之前,得加鎖,加鎖本質(zhì)是給 臨界區(qū) 加鎖,加鎖的粒度盡量要細(xì)一些
  3. 線程訪問臨界區(qū)的時候,需要先加鎖->所有線程都必須要先看到同一把鎖->鎖本身就是公共資源->鎖如何保證自己的安全?-> 加鎖和解鎖本身就是原子的!
  4. 臨界區(qū)可以是一行代碼,可以是一批代碼,
    a. 線程可能被切換嗎?當(dāng)然可能, 不要特殊化加鎖和解鎖,還有臨界區(qū)代碼。
    b. 此時線程進(jìn)行切換會有影響嗎?不會,因為在我不在期間,任何人都沒有辦法進(jìn)入臨界區(qū),因為他無法成功的申請到鎖!因為鎖被我拿走了!
  5. 這也正是體現(xiàn)互斥帶來的串行化的表現(xiàn),站在其他線程的角度,對其他線程有意義的狀態(tài)就是:鎖被我申請(持有鎖),鎖被我釋放了(不持有鎖), 原子性就體現(xiàn)在這里
  6. 解鎖的過程也被設(shè)計成為原子的!

互斥鎖的原理:

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

下面我們來根據(jù)lockunlock的偽代碼來分析一下加鎖和解鎖的過程:

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

線程A:

  1. movb $0,al 調(diào)用線程,向自己的上下文寫入0
    【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

  2. xchgb %al,mutex 將cpu的寄存器中的%al 與 內(nèi)存中的mutex 進(jìn)行交換,本質(zhì)是將共享數(shù)據(jù)交換到 自己的私有的上下文中。交換只有 一條匯編指令 ,要么沒交換,要不就交換完了,即加鎖的原子性
    【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

  3. 判斷al寄存器中的內(nèi)容是否大于0,如果大于0,證明加鎖成功。
    【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

線程B:

  1. 切換成線程B,繼續(xù)執(zhí)行前兩條指令,先將 al寄存器數(shù)據(jù)置為0,再將寄存器中的數(shù)據(jù) 與 內(nèi)存中的數(shù)據(jù)進(jìn)行交換。

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

  1. 接著判斷al寄存器中的內(nèi)容是否大于0,發(fā)現(xiàn)并不大于0,說明b申請鎖失敗,緊接著b線程被掛起等待,同時b的上下文隨著b的掛起被帶走。

  2. 當(dāng)A線程再次被切換回來時,繼續(xù)執(zhí)行上次還未執(zhí)行的判斷,發(fā)現(xiàn)al中的數(shù)據(jù)大于0,加鎖成功
    【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

  3. 線程A釋放鎖,movb $1,mutex 將內(nèi)存中mutex的數(shù)據(jù)置為1,喚醒等待Mutex的線程,此時切換成線程B

  4. 線程B執(zhí)行lock的前兩條指令,此時就可以加鎖成功了。【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器


二、可重入和線程安全

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

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

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

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

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

?? 常見的可重入的情況:

  • 不使用全局變量或靜態(tài)變量
  • 不使用用malloc或者new開辟出的空間
  • 不調(diào)用不可重入函數(shù)
  • 不返回靜態(tài)或全局?jǐn)?shù)據(jù),所有數(shù)據(jù)都有函數(shù)的調(diào)用者提供
  • 使用本地數(shù)據(jù),或者通過制作全局?jǐn)?shù)據(jù)的本地拷貝來保護(hù)全局?jǐn)?shù)據(jù)

?? 常見的不可重入的情況:

  • 調(diào)用了malloc/free函數(shù),因為malloc函數(shù)是用全局鏈表來管理堆的
  • 調(diào)用了標(biāo)準(zhǔn)I/O庫函數(shù),標(biāo)準(zhǔn)I/O庫的很多實現(xiàn)都以不可重入的方式使用全局?jǐn)?shù)據(jù)結(jié)構(gòu)
  • 可重入函數(shù)體內(nèi)使用了靜態(tài)的數(shù)據(jù)結(jié)構(gòu)

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

  • 函數(shù)是可重入的,那就是線程安全的。線程安全的函數(shù),不一定是可重入函數(shù)
  • 函數(shù)是不可重入的,那就不能由多個線程使用,有可能引發(fā)線程安全問題(如:printf 函數(shù)是不可重入的,多線程向顯示器上打印數(shù)據(jù)時,數(shù)據(jù)可能會黏在一起)
  • 如果一個函數(shù)中有全局變量,那么這個函數(shù)既不是線程安全也不是可重入的

三、線程和互斥鎖的封裝

1. 線程封裝

?? Threa.hpp

#pragma once

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

class Thread
{
public:
    typedef enum{
        NEW = 0,
        RUNNING,
        EXITED
    } ThreadStatus;
    typedef void (*func_t)(void*);

public:
    Thread(int num, func_t func, void* args) :_tid(0), _status(NEW),_func(func),_args(args)
    {
        char name[128];
        snprintf(name, 128, "thread-%d", num);
        _name = name;
    }

    int status(){ return _status; }
    string threadname(){ return _name; }

    pthread_t get_id()
    {
        if(_status == RUNNING)
            return _tid;
        else
            return 0;
    }

    static void* thread_run(void* args)
    {
        Thread* ti = static_cast<Thread*>(args);
        (*ti)();
        return nullptr;
    }

    void operator()()
    {
        if(_func != nullptr)
            _func(_args);
    }

    void run() // 封裝線程運行
    {
        int n = pthread_create(&_tid, nullptr, thread_run, this);
        if(n != 0)
            exit(-1);
        _status = RUNNING; // 線程狀態(tài)變?yōu)檫\行
    }

    void join() // 瘋轉(zhuǎn)線程等待
    {
        int n = pthread_join(_tid, nullptr);
        if(n != 0)
        {
            cout << "main thread join thread: " << _name << "error" << endl;
            return;
        }
        _status = EXITED;
    }

    ~Thread(){}
private:
    pthread_t _tid;
    string _name;
    func_t _func; // 線程未來要執(zhí)行的回調(diào)
    void* _args;
    ThreadStatus _status;
};

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

1. 互斥鎖封裝

?? lockGuard.hpp

class Mutex // 自己不維護(hù)鎖,有外部傳入
{
public:
    Mutex(pthread_mutex_t *mutex):_pmutex(mutex)
    {}
    void lock()
    {
        pthread_mutex_lock(_pmutex);
    }
    void unlock()
    {
        pthread_mutex_unlock(_pmutex);
    }
    ~Mutex()
    {}
private:
    pthread_mutex_t *_pmutex;
};

class LockGuard // 自己不維護(hù)鎖,有外部傳入
{
public:
    LockGuard(pthread_mutex_t *mutex):_mutex(mutex)
    {
        _mutex.lock();
    }
    ~LockGuard()
    {
        _mutex.unlock();
    }
private:
    Mutex _mutex;
};

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器


四、死鎖

1. 死鎖的概念

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

下面我們通過一個小故事來讓大家理解一下死鎖:

有兩個小朋友張三和李四,共同去了一家商店,想要購買一塊1塊錢的棒棒糖,但是他們兩個各自都只有五毛錢。因此張三想要李四手里的五毛錢去買棒棒糖讓自己吃,但這時候李四就不樂意了,他也想想要張三手里的五毛錢去買棒棒糖讓自己吃。因此兩個人陷入了僵局,因此買棒棒糖吃這件事情就一直無法推進(jìn)下去。

  • 兩個小朋友可以看作是兩個線程,兩個不同的小朋友可以看作兩把不同的鎖
  • 棒棒糖是臨界資源,老板就是操作系統(tǒng)
  • 想要訪問臨界資源,必須同時擁有兩把鎖

在操作系統(tǒng)中我們可以通過兩個線程的案例來理解死鎖:

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

雖然一般來說產(chǎn)生死鎖是因為兩把及兩把以上的鎖導(dǎo)致的,但是一把鎖也有可能會產(chǎn)生死鎖。


2. 死鎖的四個必要條件

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

3. 避免死鎖

  1. 不加鎖
  2. 主動釋放鎖
    (假設(shè)要有兩把鎖才能獲取臨界資源,本身有一把鎖,在多次申請另一把鎖時申請不到,就把自身的鎖釋放掉)
  3. 按照順序申請鎖
    (假設(shè)有線程A和B,線程A申請鎖時,必須保持先A再B,線程B申請鎖時,也必須保持先A再B
    當(dāng)線程A申請到A鎖時,線程B也申請到A,就不會出現(xiàn)互相申請的情況了)
  4. 控制線程統(tǒng)一釋放鎖
    (將所有線程 申請的鎖 使用一個線程 全部釋放掉,就不會出現(xiàn)死鎖了)

證明:一個線程申請的鎖,可以由另一個線程來釋放

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

using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//一個線程加鎖, 另一個線程釋放鎖

void* threadRoutine(void* args)
{
    cout << "I am a new thread" << endl;

    pthread_mutex_lock(&mutex);
    cout << "I get a mutex!" << endl;

    pthread_mutex_lock(&mutex);
    cout << "I alive again" << endl;

    return nullptr;
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRoutine, nullptr);

    sleep(3);

    cout << "main thread run begin" << endl;
    pthread_mutex_unlock(&mutex);
    cout << "main thread unlock..." << endl;
    sleep(3);

    return 0;
}

【Linux】多線程互斥與同步,Linux,linux,運維,服務(wù)器

由運行結(jié)果我們就可以看出,說明一個線程申請一把鎖,可以由另一個線程釋放。


五、線程同步

1. 線程同步的理解

互斥鎖存在的兩種不合理的情況:

  • 一個線程頻繁的申請到鎖,別人無法申請到鎖,導(dǎo)致別人饑餓的問題
  • 上述的搶票系統(tǒng),修改一下,當(dāng)票數(shù)為0時,并不會立即退出。而是等待票數(shù)的增加,在等待票數(shù)增加的過程中,線程會頻繁的申請鎖和釋放鎖。這樣的情況會導(dǎo)致資源的浪費。

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

當(dāng)我們訪問臨界資源前,需要先做臨界資源是否存在的檢測,檢測的本質(zhì)也是訪問臨界資源。那么對臨界資源的檢測也一定要在加鎖和解鎖之間。常規(guī)的方法檢測臨界資源是否就緒,就注定了我們必須頻繁地申請鎖和釋放鎖。


2. 條件變量

想要解決線程頻繁申請和釋放鎖的問題,需要做到以下兩點:

  • 不要讓線程在頻繁的檢測資源是否就緒,而是讓線程在資源未就緒時進(jìn)行等待。
  • 當(dāng)資源就緒的時候,通知等待該資源的線程,讓這些線程來進(jìn)行資源的申請和訪問。

達(dá)到以上兩點要求就是條件變量,條件變量可以通過允許線程阻塞和等待另一個線程發(fā)送信號來彌補(bǔ)互斥鎖的不足,所以互斥鎖和條件變量通常是一起使用的。

條件變量是一種線程同步機(jī)制,用于在多線程環(huán)境下實現(xiàn)線程間的協(xié)調(diào)與通信。他在處理競態(tài)條件和線程間的互斥等問題上具有重要作用。

?? 條件變量初始化

// 初始化方式一:
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
// 初始化方式二:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

參數(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:互斥量

?? 喚醒等待文章來源地址http://www.zghlxwxcb.cn/news/detail-709040.html

int pthread_cond_broadcast(pthread_cond_t *cond); // 喚醒全部的線程
int pthread_cond_signal(pthread_cond_t *cond); // 喚醒該條件變量下等待的線程
#include <iostream>
#include <cstdio>
#include <string>
#include <pthread.h>
#include <unistd.h>
using namespace std;

const int num = 5;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* active(void* args)
{
    string name = static_cast<const char*>(args);
    while(true)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond, &mutex);// pthread_cond_wait,調(diào)用的時候,會自動釋放鎖
        cout << name << "活動" << endl;
        pthread_mutex_unlock(&mutex);
    }
}

int main()
{
    pthread_t tids[num];
    for(int i = 0; i < num; i++)
    {
        char* name = new char[32];
        snprintf(name, 32, "pthread-%d", i + 1);
        pthread_create(tids + i, nullptr, active, name);
    }

    sleep(3);

    while(true)
    {
        cout << "main thread wakeup other thread..." << endl;
        pthread_cond_broadcast(&cond);

        sleep(1);
    }

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

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

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

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

相關(guān)文章

  • 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日
    瀏覽(20)
  • Linux——線程的同步與互斥

    Linux——線程的同步與互斥

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

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

    【Linux】多線程互斥與同步

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

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

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

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

    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日
    瀏覽(18)
  • 【Linux】多線程2——線程互斥與同步/多線程應(yīng)用

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

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

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

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

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

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

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

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

    2024年02月02日
    瀏覽(24)
  • 【Linux】多線程02 --- 線程的同步互斥問題及生產(chǎn)消費模型

    【Linux】多線程02 --- 線程的同步互斥問題及生產(chǎn)消費模型

    ?? 作者: 阿潤菜菜 ?? 專欄: Linux系統(tǒng)編程 線程同步互斥問題是指多線程程序中,如何保證共享資源的正確訪問和線程間的協(xié)作。 因為線程互斥是實現(xiàn)線程同步的基礎(chǔ)和前提,我們先講解線程互斥問題。 在多線程中,假設(shè)我們有一個黃牛搶票的代碼,其中有一份共享資源

    2024年02月08日
    瀏覽(17)
  • 運維 | 查看 Linux 服務(wù)器 IP 地址

    大多數(shù)在操作 Linux 系統(tǒng)時,我們經(jīng)常需要知道服務(wù)器的 IP 比便于后續(xù)的一系列操作,這時候有快速查看主機(jī) IP 的命令行操作,能夠有效的幫助我們 本章節(jié)主要記錄一些常用查看服務(wù)器 IP 的命令,希望對大家有所幫助。 查看 Linux 服務(wù)器的 IP 地址的命令大體上有以下幾種。

    2024年04月27日
    瀏覽(103)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包