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

Linux下的多線程編程:原理、工具及應(yīng)用(1)

這篇具有很好參考價(jià)值的文章主要介紹了Linux下的多線程編程:原理、工具及應(yīng)用(1)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

Linux下的多線程編程:原理、工具及應(yīng)用(1),Linux練功 初階功法,linux,運(yùn)維,服務(wù)器

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???慕斯主頁(yè)修仙—?jiǎng)e有洞天

?? ????????????????????????????????????????? ???今日夜電波:Flower of Life—陽(yáng)花

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0:34━━━━━━???──────── 4:46
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????? ? ?? ? ? ? ?? ? ????

????????????????????????????????????????關(guān)注??點(diǎn)贊??收藏您的每一次鼓勵(lì)都是對(duì)我莫大的支持??


目錄

模擬語(yǔ)言封裝Linux下多線程接口

線程互斥

前置知識(shí)

解釋為什么會(huì)產(chǎn)生上述代碼錯(cuò)誤

如何解決?加鎖!

什么是互斥鎖?

pthread_mutex_t

pthread_mutex_init

PTHREAD_MUTEX_INITIALIZER

pthread_mutex_lock

pthread_mutex_unlock

pthread_mutex_trylock(不常用)

根據(jù)如上的互斥鎖來(lái)進(jìn)行操作

通過(guò)定義全局的鎖

通過(guò)定義局部的鎖(優(yōu)雅的解決)


模擬語(yǔ)言封裝Linux下多線程接口

????????如下是我們以使用C++實(shí)現(xiàn)簡(jiǎn)單封裝:

#pragma once
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <string>
#include <functional>

template <class T>
using func_t = std::function<void(T)>;

template <class T>
class Thread
{
public:
    Thread(const std::string threadname,func_t<T> func,T data)
        :_tid(0),_threadname(threadname),_isrunning(false),_func(func),_data(data)
        {}
    
    static void* ThreadRoutine(void* args)
    {
        Thread *ts=static_cast<Thread *>(args);

        ts->_func(ts->_data);

        return nullptr;
    }

    bool Start()
    {
        int n=pthread_create(&_tid,nullptr,ThreadRoutine,this);
        if(n==0)
        {
            _isrunning=true;
            return true;
        }
        return false;
    }

    bool Join()
    {
        if(!_isrunning) return true;
        int n=pthread_join(_tid,nullptr);
        if(n==0)
        {
            _isrunning=false;
            return true;
        }
        return false;

    }

    std::string ThreadName()
    {
        return _threadname;
    }

    bool Isrunning()
    {
        return _isrunning;
    }

private:
    pthread_t _tid;
    std::string _threadname;
    bool _isrunning;
    func_t<T> _func;
    T _data;
};

????????其中成員變量存儲(chǔ)了進(jìn)程線程的ID、線程名、線程運(yùn)行狀態(tài)、線程運(yùn)行的函數(shù)以及線程傳遞的變量。實(shí)現(xiàn)了線程的構(gòu)造函數(shù)(根據(jù)線程名、傳入函數(shù)以及傳入變量構(gòu)造),實(shí)現(xiàn)了開始運(yùn)行的操作、等待線程的操作、判斷是否運(yùn)行以及返回線程名的操作。

????????重點(diǎn)說(shuō)一下ThreadRoutine這個(gè)函數(shù)!他實(shí)現(xiàn)的是配合運(yùn)行操作為pthread_create傳遞函數(shù)在執(zhí)行完后return nullptr結(jié)束線程的操作!那他為啥要定義成如下的形式呢?

    static void* ThreadRoutine(void* args)
    {
        Thread *ts=static_cast<Thread *>(args);

        ts->_func(ts->_data);

        return nullptr;
    }

????????這是因?yàn)槌蓡T函數(shù)是默認(rèn)會(huì)帶有this指針的,而我們要傳入pthread_create中規(guī)定了只能傳入void* args變量的函數(shù)。因此我們使用static讓他不具有this指針!當(dāng)然,這并不是唯一的解決辦法,我們也可以將他定義到類外,然后給在類內(nèi)聲明友元函數(shù)即可。

線程互斥

????????我們根據(jù)上面所封裝的線程庫(kù)來(lái)敘寫了如下的代碼:

#include "Thread.hpp"

std::string GetThreadName()
{
    static int number = 1;
    char name[64];
    snprintf(name, sizeof(name), "Thread-%d", number++);
    return name;
}

void Print(int num)
{
    while (num)
    {
        std::cout << "hello world: " << num-- << std::endl;
        sleep(1);
    }
}



int ticket = 10000; // 全局的共享資源


void GetTicket(std::string name)
{
    while (true)
    {
       
        if (ticket > 0) 
        {
            // 充當(dāng)搶票花費(fèi)的時(shí)間
            usleep(1000);
            printf("%s get a ticket: %d\n", name.c_str(), ticket);
            ticket--;
        }
        else
        {

            break;
        }
        // 實(shí)際情況,還有后續(xù)的動(dòng)作, TODO?
    }
}


int main()
{
    std::string name1 = GetThreadName();
    Thread<std::string> t1(name1, GetTicket, name1);

    std::string name2 = GetThreadName();
    Thread<std::string> t2(name2, GetTicket, name2);

    std::string name3 = GetThreadName();
    Thread<std::string> t3(name3, GetTicket, name3);

    std::string name4 = GetThreadName();
    Thread<std::string> t4(name4, GetTicket, name4);

    t1.Start();
    t2.Start();
    t3.Start();
    t4.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    t4.Join();

    return 0;
}

????????該代碼簡(jiǎn)單的模擬了一個(gè)搶票的場(chǎng)景,其中票數(shù)僅為tickets=10000,在tickets減為0的時(shí)候就會(huì)停止搶票。我們創(chuàng)建4個(gè)線程來(lái)模擬多人搶票的情況。按照代碼的原意,四個(gè)線程會(huì)依次搶票知道票數(shù)為0為止,但是代碼真的如我們所想的那樣嘛?

Linux下的多線程編程:原理、工具及應(yīng)用(1),Linux練功 初階功法,linux,運(yùn)維,服務(wù)器

????????從上面的現(xiàn)象我們很容易的發(fā)現(xiàn)我們的共享資源tickets發(fā)生了不該發(fā)生的操作,出現(xiàn)搶到同一張票以及多搶了的情況,出現(xiàn)了數(shù)據(jù)不一致的情況!這是為什么呢?下面先了解一些前置知識(shí):

前置知識(shí)

????????我們將任何一個(gè)時(shí)刻,只允許一個(gè)進(jìn)程正在訪問(wèn)的資源稱為臨界資源。把進(jìn)程中訪問(wèn)臨界資源的代碼叫做臨界區(qū)。例如我們上述的代碼:

Linux下的多線程編程:原理、工具及應(yīng)用(1),Linux練功 初階功法,linux,運(yùn)維,服務(wù)器

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

Linux下的多線程編程:原理、工具及應(yīng)用(1),Linux練功 初階功法,linux,運(yùn)維,服務(wù)器

????????原子性(后面討論如何實(shí)現(xiàn)):不會(huì)被任何調(diào)度機(jī)制打斷的操作,該操作只有兩態(tài),要么完成,要么未完成。

?

解釋為什么會(huì)產(chǎn)生上述代碼錯(cuò)誤

????????請(qǐng)先看以下代碼:

#include<iostream>

int main()
{
	int a = 10;
	a++;
	return 0;
}

????????很平常的一段代碼,但是這也是我們常出現(xiàn)錯(cuò)誤代碼中訪問(wèn)臨界資源的代碼。我們可以從反匯編中看出問(wèn)題:

Linux下的多線程編程:原理、工具及應(yīng)用(1),Linux練功 初階功法,linux,運(yùn)維,服務(wù)器

????????可以看到通過(guò)反匯編,我們發(fā)現(xiàn)本來(lái)是一段代碼的操作,在底層居然被翻譯成了三條操作。這也就違背了我們的原子性!這三條語(yǔ)句在多線程并發(fā)訪問(wèn)的時(shí)候都有可能會(huì)被中斷!大致圖解:

Linux下的多線程編程:原理、工具及應(yīng)用(1),Linux練功 初階功法,linux,運(yùn)維,服務(wù)器

????????當(dāng)我們知道實(shí)際上訪問(wèn)臨界資源時(shí)是有三步操作后,我們就可以理解為什么會(huì)產(chǎn)生如上的錯(cuò)誤了!看完如下的例子就明白了:假設(shè)現(xiàn)在我們啟動(dòng)了兩個(gè)線程,他們都要執(zhí)行如上的三步。線程1先是訪問(wèn)該臨界資源,但是在他執(zhí)行完幾次完整的訪問(wèn)(3步都走完)后,它的時(shí)間片用完了,恰好此時(shí)該線程卡在第二步的--操作,此時(shí)他要保存上下文,eax中存儲(chǔ)的值為7,然后輪到下一個(gè)進(jìn)程執(zhí)行。下一個(gè)進(jìn)程也是執(zhí)行完整了幾步,恰好也是在第二步的時(shí)候他的時(shí)間片用完了,此時(shí)他也要保存上下文,eax中存儲(chǔ)的值為3。接著輪到第一個(gè)線程,他需要恢復(fù)上下文啊,因此,從第三步開始將eax返回內(nèi)存中,此時(shí)!count又變回了7!??!

????????看完上面的例子你大概就明白了,為什么上述代碼互產(chǎn)生錯(cuò)誤的原因。因?yàn)槎嗑€程并發(fā)訪問(wèn)全部int,不是原子的?。。?huì)有數(shù)據(jù)不一致的并發(fā)訪問(wèn)問(wèn)題!

????????看完上述的解釋是不是以為完了?當(dāng)然沒有!我們都知道在CPU中我們存在著:算術(shù)運(yùn)算、邏輯運(yùn)算、處理內(nèi)外中斷、控制單元的操作。在上述出錯(cuò)代碼中我們還存在著if的判斷語(yǔ)句,這就是一種邏輯運(yùn)算,底層是需要兩步的處理:1、加載如寄存器。2、判斷。他也不是原子的!也會(huì)出現(xiàn)會(huì)有數(shù)據(jù)不一致的并發(fā)訪問(wèn)問(wèn)題!因此,我們根本就不知道每個(gè)進(jìn)程的具體執(zhí)行狀況!這也是為什么該數(shù)據(jù)會(huì)減到負(fù)數(shù)的原因,因?yàn)橛锌赡躨f判斷都認(rèn)為是符合條件的!但是實(shí)際寄存器中的值確是不符合的!

如何解決?加鎖!

什么是互斥鎖?

????????互斥鎖是一種同步機(jī)制,用于確保在多線程環(huán)境中共享資源的安全訪問(wèn)

????????互斥鎖的核心作用是防止多個(gè)線程同時(shí)訪問(wèn)和修改共享資源,從而避免數(shù)據(jù)競(jìng)爭(zhēng)和不一致的問(wèn)題。以下是互斥鎖的一些關(guān)鍵特性和概念:

  1. 排他性:互斥鎖確保在任何時(shí)刻,只有一個(gè)線程能夠持有鎖并訪問(wèn)共享資源。當(dāng)一個(gè)線程獲得鎖時(shí),其他線程必須等待直到鎖被釋放。
  2. 同步原語(yǔ):互斥鎖是一種同步原語(yǔ),它通常與條件變量、信號(hào)量等其他同步機(jī)制一起使用,以實(shí)現(xiàn)復(fù)雜的線程間協(xié)作和通信。
  3. 初始化:在使用互斥鎖之前,需要對(duì)其進(jìn)行初始化,這可以通過(guò)pthread_mutex_init函數(shù)完成,也可以靜態(tài)地通過(guò)PTHREAD_MUTEX_INITIALIZER宏來(lái)初始化一個(gè)互斥鎖變量。
  4. 加鎖與解鎖:線程在訪問(wèn)共享資源前需要對(duì)互斥鎖進(jìn)行加鎖(上鎖),訪問(wèn)完成后需要釋放互斥鎖(解鎖)。這一過(guò)程通常通過(guò)pthread_mutex_lockpthread_mutex_unlock函數(shù)來(lái)實(shí)現(xiàn)。
  5. 性能考量:互斥鎖可能導(dǎo)致進(jìn)程睡眠和喚醒,以及上下文切換,這些都會(huì)帶來(lái)一定的性能開銷。因此,互斥鎖適用于加鎖時(shí)間較長(zhǎng)的場(chǎng)景,以減少頻繁的鎖爭(zhēng)用和上下文切換。
  6. 銷毀:當(dāng)互斥鎖不再使用時(shí),應(yīng)當(dāng)通過(guò)pthread_mutex_destroy函數(shù)進(jìn)行銷毀,以避免資源泄漏。

pthread_mutex_t

? ? pthread_mutex_t是一個(gè)數(shù)據(jù)類型,用于表示互斥鎖(Mutex)對(duì)象。在多線程編程中,互斥鎖是一種同步機(jī)制,用于保護(hù)共享資源,防止多個(gè)線程同時(shí)訪問(wèn)和修改這些資源,從而避免數(shù)據(jù)競(jìng)爭(zhēng)和不一致的問(wèn)題。

???pthread_mutex_t類型的變量通常用于聲明一個(gè)互斥鎖對(duì)象,并使用pthread_mutex_init函數(shù)進(jìn)行初始化。初始化后,可以使用pthread_mutex_lock函數(shù)對(duì)互斥鎖進(jìn)行加鎖操作,使用pthread_mutex_unlock函數(shù)進(jìn)行解鎖操作。

????????需要注意的是,在使用完互斥鎖后,應(yīng)該及時(shí)銷毀它,以避免資源泄漏。

pthread_mutex_init

????pthread_mutex_init是一個(gè)用于初始化互斥鎖的函數(shù),它是POSIX線程庫(kù)(Pthreads)中的一部分?;コ怄i是一種同步原語(yǔ),用于保護(hù)共享資源,防止多個(gè)線程同時(shí)訪問(wèn)。

????????函數(shù)原型:

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

????????參數(shù)說(shuō)明:

  • mutex:指向要初始化的互斥鎖對(duì)象的指針。
  • attr:指向互斥鎖屬性對(duì)象的指針,可以設(shè)置為NULL,表示使用默認(rèn)屬性。

????????返回值:

  • 成功時(shí),返回0;
  • 失敗時(shí),返回一個(gè)非零錯(cuò)誤碼。

PTHREAD_MUTEX_INITIALIZER

? ? PTHREAD_MUTEX_INITIALIZER是一個(gè)宏定義,用于初始化一個(gè)互斥鎖對(duì)象。它通常與靜態(tài)分配的互斥鎖變量一起使用,以確保在多線程環(huán)境中對(duì)共享資源的訪問(wèn)是安全的。

????????該宏定義的作用是將互斥鎖對(duì)象的值設(shè)置為默認(rèn)狀態(tài),以便在程序啟動(dòng)時(shí)立即可用。具體來(lái)說(shuō),它將互斥鎖對(duì)象的類型設(shè)置為pthread_mutex_t,并將其屬性設(shè)置為默認(rèn)值(通常是快速互斥鎖)。

pthread_mutex_lock

????pthread_mutex_lock是一個(gè)函數(shù),用于對(duì)互斥鎖進(jìn)行加鎖操作。它的作用是確保在多線程環(huán)境中,只有一個(gè)線程可以訪問(wèn)共享資源,從而避免數(shù)據(jù)競(jìng)爭(zhēng)和不一致的問(wèn)題。

????????函數(shù)原型:

int pthread_mutex_lock(pthread_mutex_t *mutex);

????????其中,mutex參數(shù)是一個(gè)指向互斥鎖對(duì)象的指針。

????????當(dāng)一個(gè)線程調(diào)用pthread_mutex_lock函數(shù)時(shí),它會(huì)嘗試獲取互斥鎖。如果互斥鎖當(dāng)前未被其他線程持有,則該線程成功獲取互斥鎖并繼續(xù)執(zhí)行后續(xù)代碼。如果互斥鎖已經(jīng)被其他線程持有,則該線程會(huì)被阻塞,直到互斥鎖被釋放為止。

????????一旦線程成功獲取互斥鎖,其他試圖獲取該互斥鎖的線程將會(huì)被阻塞,直到當(dāng)前持有互斥鎖的線程調(diào)用pthread_mutex_unlock函數(shù)釋放互斥鎖。

????????需要注意的是,在使用完互斥鎖后,應(yīng)該及時(shí)調(diào)用pthread_mutex_unlock函數(shù)來(lái)釋放互斥鎖,以避免死鎖或資源泄漏的情況發(fā)生。

pthread_mutex_unlock

? ? pthread_mutex_unlock是一個(gè)函數(shù),用于對(duì)互斥鎖進(jìn)行解鎖操作。它的作用是釋放當(dāng)前線程持有的互斥鎖,以便其他線程可以獲取該互斥鎖并訪問(wèn)共享資源。

????????函數(shù)原型:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

????????其中,mutex參數(shù)是一個(gè)指向互斥鎖對(duì)象的指針。

????????當(dāng)一個(gè)線程調(diào)用pthread_mutex_unlock函數(shù)時(shí),它會(huì)嘗試釋放當(dāng)前線程持有的互斥鎖。如果當(dāng)前線程確實(shí)持有該互斥鎖,則該函數(shù)會(huì)成功釋放互斥鎖并返回0;否則,該函數(shù)會(huì)返回錯(cuò)誤碼。

????????需要注意的是,在使用完互斥鎖后,應(yīng)該及時(shí)調(diào)用pthread_mutex_unlock函數(shù)來(lái)釋放互斥鎖,以避免死鎖或資源泄漏的情況發(fā)生。

pthread_mutex_trylock(不常用)

? ? pthread_mutex_trylock是一個(gè)函數(shù),用于嘗試對(duì)互斥鎖進(jìn)行加鎖操作。它的作用是嘗試獲取互斥鎖,如果互斥鎖當(dāng)前未被其他線程持有,則該線程成功獲取互斥鎖并繼續(xù)執(zhí)行后續(xù)代碼;如果互斥鎖已經(jīng)被其他線程持有,則該線程不會(huì)阻塞,而是立即返回錯(cuò)誤碼。

????????函數(shù)原型:

int pthread_mutex_trylock(pthread_mutex_t *mutex);

????????其中,mutex參數(shù)是一個(gè)指向互斥鎖對(duì)象的指針。

????????當(dāng)一個(gè)線程調(diào)用pthread_mutex_trylock函數(shù)時(shí),它會(huì)嘗試獲取互斥鎖。如果互斥鎖當(dāng)前未被其他線程持有,則該線程成功獲取互斥鎖并繼續(xù)執(zhí)行后續(xù)代碼;如果互斥鎖已經(jīng)被其他線程持有,則該線程不會(huì)阻塞,而是立即返回錯(cuò)誤碼。

????????需要注意的是,在使用完互斥鎖后,應(yīng)該及時(shí)調(diào)用pthread_mutex_unlock函數(shù)來(lái)釋放互斥鎖,以避免死鎖或資源泄漏的情況發(fā)生。

根據(jù)如上的互斥鎖來(lái)進(jìn)行操作

通過(guò)定義全局的鎖

????????如下我們根據(jù)互斥鎖來(lái)解決上述的問(wèn)題:

#include <iostream>
#include <unistd.h>
#include <vector>
#include <cstdio>
#include "Thread.hpp"

// 應(yīng)用方的視角
std::string GetThreadName()
{
    static int number = 1;
    char name[64];
    snprintf(name, sizeof(name), "Thread-%d", number++);
    return name;
}

void Print(int num)
{
    while (num)
    {
        std::cout << "hello world: " << num-- << std::endl;
        sleep(1);
    }
}

int ticket = 10000; // 全局的共享資源
// 共享資源了
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 鎖就有了,被定義并初始化了,這個(gè)鎖也是全局的哦??!

// 加鎖:
// 1. 我們要盡可能的給少的代碼塊加鎖
// 2. 一般加鎖,都是給臨界區(qū)加鎖
void GetTicket(std::string name)
{
    while (true)
    {
        // 2. 是由程序員自己保證的!規(guī)則都必須先申請(qǐng)鎖
        // 3. 根據(jù)互斥的定義,任何時(shí)刻,只允許一個(gè)線程申請(qǐng)鎖成功!多個(gè)線程申請(qǐng)鎖失敗,失敗的線程怎么辦?在mutex上進(jìn)行阻塞,本質(zhì)就是等待!
        pthread_mutex_lock(&mutex); // 1. 申請(qǐng)鎖本身是安全的,原子的,為什么?
        if (ticket > 0) // 4. 一個(gè)線程在臨界區(qū)中訪問(wèn)臨界資源的時(shí)候,可不可能發(fā)生切換?可能,完全允許??!
        {
            // 充當(dāng)搶票花費(fèi)的時(shí)間
            usleep(1000);
            printf("%s get a ticket: %d\n", name.c_str(), ticket);
            ticket--;
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
        // 實(shí)際情況,還有后續(xù)的動(dòng)作, TODO?
    }
}


int main()
{
    std::string name1 = GetThreadName();
    Thread<std::string> t1(name1, GetTicket, name1);

    std::string name2 = GetThreadName();
    Thread<std::string> t2(name2, GetTicket, name2);

    std::string name3 = GetThreadName();
    Thread<std::string> t3(name3, GetTicket, name3);

    std::string name4 = GetThreadName();
    Thread<std::string> t4(name4, GetTicket, name4);

    t1.Start();
    t2.Start();
    t3.Start();
    t4.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    t4.Join();

    return 0;
}

Linux下的多線程編程:原理、工具及應(yīng)用(1),Linux練功 初階功法,linux,運(yùn)維,服務(wù)器

????????可以看到我們成功的解決了上述的問(wèn)題,但是加了互斥鎖也讓我們的執(zhí)行速度相對(duì)于上面的執(zhí)行速度變慢了許多。這是因?yàn)椋?span style="color:#956fe7;">當(dāng)一個(gè)線程持有互斥鎖時(shí),其他試圖訪問(wèn)相同共享資源的線程必須等待,直到鎖被釋放。這種等待會(huì)導(dǎo)致線程阻塞,減少了并行執(zhí)行的機(jī)會(huì)。當(dāng)線程在等待鎖的過(guò)程中,操作系統(tǒng)可能會(huì)將其置于睡眠狀態(tài),并在鎖可用時(shí)再次喚醒它。這種從睡眠到喚醒的過(guò)程涉及到上下文切換,這是一種相對(duì)耗時(shí)的操作。頻繁的上下文切換會(huì)顯著增加程序的執(zhí)行時(shí)間。

通過(guò)定義局部的鎖(優(yōu)雅的解決)

????????如下我們定義一個(gè)LockGuard.hpp的文件,該文件封裝了一個(gè)可以通過(guò)構(gòu)造以及析構(gòu)完成對(duì)應(yīng)的加鎖以及解鎖的操作:

#pragma once

#include <pthread.h>

// 不定義鎖,默認(rèn)認(rèn)為外部會(huì)給我們傳入鎖對(duì)象
class Mutex
{
public:
    Mutex(pthread_mutex_t *lock):_lock(lock)
    {}
    void Lock()
    {
        pthread_mutex_lock(_lock);
    }
    void Unlock()
    {
        pthread_mutex_unlock(_lock);
    }
    ~Mutex()
    {}

private:
    pthread_mutex_t *_lock;
};

class LockGuard
{
public:
    LockGuard(pthread_mutex_t *lock): _mutex(lock)
    {
        _mutex.Lock();
    }
    ~LockGuard()
    {
        _mutex.Unlock();
    }
private:
    Mutex _mutex;
};

????????主函數(shù):

????????可以看到我們?cè)谂R界區(qū)中定義了上述LockGuard.hpp的lockguard變量,我們可以通過(guò)上述的構(gòu)造以及析構(gòu)進(jìn)行加鎖、解鎖。需要注意的是:臨界區(qū)代碼中可以發(fā)現(xiàn)我們使用了一個(gè){}來(lái)括起來(lái),這是表示代碼塊的意思,可以理解變量同在函數(shù)棧幀中一樣。我們通過(guò)新定義的ThreadData類來(lái)傳遞給之前封裝的多線程接口,優(yōu)雅的實(shí)現(xiàn)了如下的代碼:

#include <iostream>
#include <string>
#include <unistd.h>
#include <vector>
#include <cstdio>
#include "Thread.hpp"
#include "LockGuard.hpp"

// 應(yīng)用方的視角
std::string GetThreadName()
{
    static int number = 1;
    char name[64];
    snprintf(name, sizeof(name), "Thread-%d", number++);
    return name;
}

void Print(int num)
{
    while (num)
    {
        std::cout << "hello world: " << num-- << std::endl;
        sleep(1);
    }
}

class ThreadData
{
public:
    ThreadData(const std::string &name, pthread_mutex_t *lock)
    : threadname(name), pmutex(lock)
    {}
public:
    std::string threadname;
    pthread_mutex_t *pmutex;
};

int ticket = 10000; // 全局的共享資源

void GetTicket(ThreadData *td)
{
    while (true)
    {
        // 非臨界區(qū)代碼!
        // 2. 是由程序員自己保證的!規(guī)則都必須先申請(qǐng)鎖
        // 3. 根據(jù)互斥的定義,任何時(shí)刻,只允許一個(gè)線程申請(qǐng)鎖成功!多個(gè)線程申請(qǐng)鎖失敗,失敗的線程怎么辦?在mutex上進(jìn)行阻塞,本質(zhì)就是等待!
       
        {
             LockGuard lockguard(td->pmutex);
            if (ticket > 0) // 4. 一個(gè)線程在臨界區(qū)中訪問(wèn)臨界資源的時(shí)候,可不可能發(fā)生切換?可能,完全允許!!
            {
                // 充當(dāng)搶票花費(fèi)的時(shí)間
                usleep(1000);
                printf("%s get a ticket: %d\n", td->threadname.c_str(), ticket);
                ticket--;
           
            }
            else
            {
               
                break;
            }
        }
        // 非臨界區(qū)代碼!
        // 實(shí)際情況,還有后續(xù)的動(dòng)作, TODO?
    }
}

int main()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, nullptr);

    std::string name1 = GetThreadName();
    ThreadData *td = new ThreadData(name1, &mutex);
    Thread<ThreadData *> t1(name1, GetTicket, td);

    std::string name2 = GetThreadName();
    ThreadData *td2 = new ThreadData(name2, &mutex);
    Thread<ThreadData *> t2(name2, GetTicket, td2);

    std::string name3 = GetThreadName();
    ThreadData *td3 = new ThreadData(name3, &mutex);
    Thread<ThreadData *> t3(name3, GetTicket, td3);

    std::string name4 = GetThreadName();
    ThreadData *td4 = new ThreadData(name4, &mutex);
    Thread<ThreadData *> t4(name4, GetTicket, td4);

    t1.Start();
    t2.Start();
    t3.Start();
    t4.Start();

    t1.Join();
    t2.Join();
    t3.Join();
    t4.Join();

    pthread_mutex_destroy(&mutex);
    delete td;
    delete td2;
    delete td3;
    delete td4;

    return 0;
}

????????一些知識(shí)點(diǎn)匯總:

// 加鎖:
// 1. 我們要盡可能的給少的代碼塊加鎖
// 2. 一般加鎖,都是給臨界區(qū)加鎖
// 3. 個(gè)別系統(tǒng),搶票代碼會(huì)出現(xiàn)很多的票被同一個(gè)線程搶完了
// 4. 多線程運(yùn)行,同一份資源,有線程長(zhǎng)時(shí)間無(wú)法擁有,饑餓問(wèn)題
// 5. 要解決饑餓問(wèn)題,要讓線程執(zhí)行的時(shí)候,具備一定的順序性 --- 同步

Linux下的多線程編程:原理、工具及應(yīng)用(1),Linux練功 初階功法,linux,運(yùn)維,服務(wù)器

?


?????????????????????????感謝你耐心的看到這里?( ′???` )比心,如有哪里有錯(cuò)誤請(qǐng)?zhí)咭荒_作者o(╥﹏╥)o!?

????????????????????????????????? ? ? ?Linux下的多線程編程:原理、工具及應(yīng)用(1),Linux練功 初階功法,linux,運(yùn)維,服務(wù)器

????????????????????????????????????????????????????????????????????????給個(gè)三連再走嘛~??文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-840838.html

到了這里,關(guān)于Linux下的多線程編程:原理、工具及應(yīng)用(1)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(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)文章

  • Qt的多線程編程

    Qt的多線程編程

    并發(fā) 當(dāng)有多個(gè)線程在操作時(shí),如果系統(tǒng) 只有一個(gè)CPU ,則它根本不可能真正同時(shí)進(jìn)行一個(gè)以上的線程,它只能把 CPU運(yùn)行時(shí)間劃分成若干個(gè)時(shí)間段,再將時(shí)間段分配給各個(gè)線程執(zhí)行,在一個(gè)時(shí)間段的線程代碼運(yùn)行時(shí),其他線程處于掛起狀態(tài)。 雖然看起來(lái)所有 線程都是一起執(zhí)行

    2024年02月08日
    瀏覽(17)
  • “深入理解Java的多線程編程“

    多線程編程是指在一個(gè)程序中同時(shí)運(yùn)行多個(gè)線程,以提高程序的并發(fā)性和性能。Java是一門支持多線程編程的強(qiáng)大編程語(yǔ)言,提供了豐富的多線程相關(guān)類和接口。 在Java中,可以通過(guò)以下方式實(shí)現(xiàn)多線程編程: 繼承Thread類:創(chuàng)建一個(gè)繼承自Thread類的子類,并重寫run()方法,在

    2024年02月13日
    瀏覽(53)
  • C# 中的多線程和異步編程

    C# 中的多線程和異步編程

    最近在看代碼的過(guò)程中,發(fā)現(xiàn)有很多地方涉及到多線程、異步編程,這是比較重要且常用的知識(shí)點(diǎn),而本人在這方面還理解尚淺,因此開始全面學(xué)習(xí)C#中的多線程和異步編程,文中部分內(nèi)容摘抄自一位前輩的網(wǎng)站:網(wǎng)址鏈接,為了更便于理解和學(xué)習(xí),本人還在個(gè)別地方做了一

    2023年04月08日
    瀏覽(22)
  • Android中的多線程編程與異步處理

    Android中的多線程編程與異步處理

    在移動(dòng)應(yīng)用開發(fā)中,用戶體驗(yàn)是至關(guān)重要的。一個(gè)流暢、高效的應(yīng)用能夠吸引用戶并提升用戶滿意度。然而,移動(dòng)應(yīng)用面臨著處理復(fù)雜業(yè)務(wù)邏輯、響應(yīng)用戶輸入、處理網(wǎng)絡(luò)請(qǐng)求等多個(gè)任務(wù)的挑戰(zhàn)。為了確保應(yīng)用的性能和用戶體驗(yàn),多線程編程和異步處理成為了不可或缺的技術(shù)

    2024年02月11日
    瀏覽(23)
  • Python小姿勢(shì) - Python的多線程編程

    Python小姿勢(shì) - Python的多線程編程

    Python的多線程編程 Python的多線程編程提供了一個(gè)非常簡(jiǎn)單的方法來(lái)讓一個(gè)Python程序同時(shí)運(yùn)行多個(gè)任務(wù)。這個(gè)方法通過(guò)創(chuàng)建新的線程來(lái)實(shí)現(xiàn),線程可以被視為一個(gè)單獨(dú)的執(zhí)行流程。 為了創(chuàng)建一個(gè)新線程,我們需要使用Python的_thread模塊中的start_new_thread()函數(shù)。它需要兩個(gè)參數(shù):

    2024年02月04日
    瀏覽(18)
  • 一文讀懂flutter線程: 深入了解Flutter中的多線程編程

    一文讀懂flutter線程: 深入了解Flutter中的多線程編程

    在移動(dòng)應(yīng)用開發(fā)領(lǐng)域,F(xiàn)lutter已經(jīng)成為了一個(gè)備受歡迎的框架,用于創(chuàng)建高性能、跨平臺(tái)的應(yīng)用程序。Flutter的一個(gè)關(guān)鍵特性是其能夠輕松處理多線程編程,以改進(jìn)應(yīng)用程序的性能和響應(yīng)性。本文將深入探討Flutter中的多線程編程,包括為什么需要多線程、如何在Flutter中創(chuàng)建和管

    2024年01月20日
    瀏覽(25)
  • 如何在Python編程中應(yīng)用Linux環(huán)境下的框架,以實(shí)現(xiàn)高效算法?

    python是一種廣泛使用的編程語(yǔ)言,能夠幫助開發(fā)人員快速開發(fā)高效的算法。與此同時(shí),linux環(huán)境下提供了許多優(yōu)秀的框架,可以進(jìn)一步提高Python編程的效率。本文將介紹如何在Python編程中應(yīng)用Linux環(huán)境下的框架,以實(shí)現(xiàn)高效算法。 一、Python和Linux環(huán)境的優(yōu)勢(shì) Python是一種易學(xué)易

    2024年02月05日
    瀏覽(34)
  • 深入淺出Java的多線程編程——第二篇

    深入淺出Java的多線程編程——第二篇

    目錄 前情回顧 1. 中斷一個(gè)線程 1.1 中斷的API 1.2 小結(jié) 2. 等待一個(gè)線程 ?2.1 等待的API 3. 線程的狀態(tài) 3.1 貫徹線程的所有狀態(tài) 3.2 線程狀態(tài)和狀態(tài)轉(zhuǎn)移的意義 4.?多線程帶來(lái)的的風(fēng)險(xiǎn)-線程安全 (重點(diǎn)) 4.1 觀察線程不安全 4.2 線程安全的概念 4.3 線程不安全的原因 4.3.1 修改共享數(shù)據(jù)

    2024年02月07日
    瀏覽(41)
  • 編程小白的自學(xué)筆記八(python中的多線程)

    ?編程小白的自學(xué)筆記七(python中類的繼承) 編程小白的自學(xué)筆記六(python中類的靜態(tài)方法和動(dòng)態(tài)方法)? 編程小白的自學(xué)筆記五(Python類的方法)? 編程小白的自學(xué)筆記四(正則表達(dá)式模塊search函數(shù))? 編程小白的自學(xué)筆記三(Python正則表達(dá)式)? 目錄 系列文章目錄 前言

    2024年02月16日
    瀏覽(26)
  • 目標(biāo)檢測(cè)YOLO實(shí)戰(zhàn)應(yīng)用案例100講-面向惡劣環(huán)境下的多模態(tài) 行人識(shí)別

    目錄 前言 國(guó)內(nèi)外研究現(xiàn)狀? 可見光行人目標(biāo)識(shí)別? 紅外行人目標(biāo)識(shí)別?

    2024年02月07日
    瀏覽(29)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包