? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???慕斯主頁(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為止,但是代碼真的如我們所想的那樣嘛?
????????從上面的現(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ū)。例如我們上述的代碼:
????????互斥:任何時(shí)刻,互斥保證有且只有一個(gè)執(zhí)行流進(jìn)入臨界區(qū),訪問(wèn)臨界資源,通常對(duì)臨界資源起保護(hù)作用 。圖解如下:
????????原子性(后面討論如何實(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)題:
????????可以看到通過(guò)反匯編,我們發(fā)現(xiàn)本來(lái)是一段代碼的操作,在底層居然被翻譯成了三條操作。這也就違背了我們的原子性!這三條語(yǔ)句在多線程并發(fā)訪問(wèn)的時(shí)候都有可能會(huì)被中斷!大致圖解:
????????當(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)鍵特性和概念:
- 排他性:互斥鎖確保在任何時(shí)刻,只有一個(gè)線程能夠持有鎖并訪問(wèn)共享資源。當(dāng)一個(gè)線程獲得鎖時(shí),其他線程必須等待直到鎖被釋放。
- 同步原語(yǔ):互斥鎖是一種同步原語(yǔ),它通常與條件變量、信號(hào)量等其他同步機(jī)制一起使用,以實(shí)現(xiàn)復(fù)雜的線程間協(xié)作和通信。
- 初始化:在使用互斥鎖之前,需要對(duì)其進(jìn)行初始化,這可以通過(guò)
pthread_mutex_init
函數(shù)完成,也可以靜態(tài)地通過(guò)PTHREAD_MUTEX_INITIALIZER
宏來(lái)初始化一個(gè)互斥鎖變量。- 加鎖與解鎖:線程在訪問(wèn)共享資源前需要對(duì)互斥鎖進(jìn)行加鎖(上鎖),訪問(wèn)完成后需要釋放互斥鎖(解鎖)。這一過(guò)程通常通過(guò)
pthread_mutex_lock
和pthread_mutex_unlock
函數(shù)來(lái)實(shí)現(xiàn)。- 性能考量:互斥鎖可能導(dǎo)致進(jìn)程睡眠和喚醒,以及上下文切換,這些都會(huì)帶來(lái)一定的性能開銷。因此,互斥鎖適用于加鎖時(shí)間較長(zhǎng)的場(chǎng)景,以減少頻繁的鎖爭(zhēng)用和上下文切換。
- 銷毀:當(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;
}
????????可以看到我們成功的解決了上述的問(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í)候,具備一定的順序性 --- 同步
?
?????????????????????????感謝你耐心的看到這里?( ′???` )比心,如有哪里有錯(cuò)誤請(qǐng)?zhí)咭荒_作者o(╥﹏╥)o!?
????????????????????????????????? ? ? ?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-840838.html
????????????????????????????????????????????????????????????????????????給個(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)!