欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于線程同步與Mutex的使用

 更新時(shí)間:2025年05月30日 08:40:56   作者:今夜有雨.  
這篇文章主要介紹了關(guān)于線程同步與Mutex的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、什么是線程同步?

  • 線程同步(Thread Synchronization)是多線程編程中的一個(gè)重要概念,它指的是通過(guò)一定的機(jī)制來(lái)控制多個(gè)線程之間的執(zhí)行順序,以確保它們能夠正確地訪問(wèn)和修改共享資源,從而避免數(shù)據(jù)競(jìng)爭(zhēng)和不一致性問(wèn)題。
  • 在多線程環(huán)境中,多個(gè)線程可能同時(shí)訪問(wèn)和修改共享資源(如變量,數(shù)據(jù)結(jié)構(gòu)或文件等)。如果沒(méi)有適當(dāng)?shù)耐綑C(jī)制,這些線程可能會(huì)以不可預(yù)測(cè)的順序執(zhí)行,導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)、臟讀、臟寫(xiě)或其他不可預(yù)期的行為。線程同步的目標(biāo)就是確保線程之間的有序執(zhí)行,以維護(hù)數(shù)據(jù)的一致性和完整性。

為什么要同步呢?

如果在同一時(shí)刻,僅只有一個(gè)線程訪問(wèn)某個(gè)變量(臨界資源),不會(huì)存在臟數(shù)據(jù)的問(wèn)題,但是,如果同一時(shí)刻有多個(gè)線程,同時(shí)訪問(wèn)同一個(gè)臨界資源的時(shí)候,就會(huì)存在 問(wèn)題,如何來(lái)解決這個(gè)問(wèn)題呢?這就提出了“線程同步”這個(gè)概念。

以下是一個(gè)簡(jiǎn)單的例子:

那么我們有沒(méi)有什么辦法,控制這些線程對(duì)臨界資源的訪問(wèn)呢?想個(gè)什么辦法才能使它們之間不亂套呢?這就該線程同步機(jī)制登場(chǎng)了

二、線程同步機(jī)制

C++中提供了多種線程同步機(jī)制,常用的方法包括:

  1. 互斥鎖(Mutex):互斥鎖是最常用的線程同步機(jī)制之一,當(dāng)一個(gè)線程想要訪問(wèn)共享資源時(shí),它首先會(huì)嘗試獲取與該資源關(guān)聯(lián)的互斥鎖如果鎖已經(jīng)被其他線程持有,則該線程將被阻塞,直到鎖被釋放。這樣可以確保在任何時(shí)候只有一個(gè)線程能夠訪問(wèn)共享資源。
  2. 條件變量(Condition Variable):條件變量用于使線程在滿(mǎn)足某個(gè)條件之前等待,它通常與互斥鎖一起使用,以便在等待條件成立時(shí)釋放鎖,并在條件成立時(shí)重新獲取鎖。這允許線程在等待期間不占用鎖,從而提高并發(fā)性能。
  3. 信號(hào)量(Semaphore):信號(hào)量是一種通用的線程同步機(jī)制,它允許多個(gè)線程同時(shí)訪問(wèn)共享資源,但限制同時(shí)訪問(wèn)的線程數(shù)量。信號(hào)量?jī)?nèi)部維護(hù)一個(gè)計(jì)數(shù)器,用于表示可用資源的數(shù)量。當(dāng)線程需要訪問(wèn)資源時(shí),它會(huì)嘗試減少計(jì)數(shù)器的值;當(dāng)線程釋放資源時(shí),它會(huì)增加計(jì)數(shù)器的值。當(dāng)計(jì)數(shù)器的值小于零時(shí),嘗試獲取資源的線程將被阻塞。
  4. 原子操作(Atomic Operation):原子操作時(shí)不可中斷的操作,即在執(zhí)行過(guò)程中不會(huì)被其他線程打斷。C++11及以后的版本提供了頭文件,其中包含了一系列原子操作的函數(shù)和類(lèi)。這些原子操作可以用于安全地更新共享數(shù)據(jù),而無(wú)需是同互斥鎖等同步機(jī)制。

三、互斥鎖(Mutex)

互斥(Mutex)是一種同步機(jī)制,同于保護(hù)共享資源,放置多個(gè)線程同時(shí)訪問(wèn)和修改同一資源,從而引起數(shù)據(jù)競(jìng)爭(zhēng)(data race)和不一致性。

當(dāng)一個(gè)線程想要訪問(wèn)某個(gè)共享資源時(shí),它首先會(huì)嘗試獲取與該資源關(guān)聯(lián)的互斥鎖(mutex)。如果互斥鎖已經(jīng)被其他線程持有(即被鎖定),則該線程將被阻塞,直到互斥鎖被釋放(即被解鎖)。一旦線程成功獲取到互斥鎖,它就可以安全地訪問(wèn)共享資源,并在訪問(wèn)完成后釋放互斥鎖,以便其他線程可以獲取該鎖并訪問(wèn)資源。 

互斥鎖通常具有以下幾個(gè)特性:

  1. 互斥性:任意時(shí)刻只有一個(gè)線程可以持有某個(gè)互斥鎖。
  2. 原子性:對(duì)互斥鎖的獲取和釋放操作時(shí)原子的,即在執(zhí)行這些操作時(shí)不會(huì)被其他線程打斷。
  3. 可重入性:某些互斥鎖類(lèi)型(如遞歸鎖)允許同一線程多次獲取同一個(gè)鎖,但通常不建議這樣做,因?yàn)樗鼤?huì)增加死鎖的風(fēng)險(xiǎn)。
  4. 非阻塞性:雖然互斥鎖本身是一種阻塞性同步機(jī)制,但某些高級(jí)實(shí)現(xiàn)(如嘗試鎖)允許線程在無(wú)法立即獲取鎖時(shí)繼續(xù)執(zhí)行其他任務(wù),而不是被阻塞。
#include <iostream>
#include <thread>
using namespace std;
//共享變量,沒(méi)有互斥鎖或原子操作
int counter = 0;

//線程函數(shù)  對(duì)counter進(jìn)行自增操作
void increment_counter(int times)
{
    for(int i=0;i<times;++i)
    {
        //這是一個(gè)數(shù)據(jù)競(jìng)爭(zhēng),因?yàn)槎鄠€(gè)線程可能同時(shí)執(zhí)行這行代碼
        counter++;
    }
}

int main()
{
    //創(chuàng)建兩個(gè)線程,每個(gè)線程對(duì)counter自增100000次
    thread t1(increment_counter,100000);
    thread t2(increment_counter,100000);

    //等待兩個(gè)線程完成
    t1.join();
    t2.join();

    //輸出結(jié)果,這個(gè)結(jié)果可能不是200000
    cout<<"最終結(jié)果:"<<counter<<endl;

    return 0;
}

在以上的這個(gè)例子中,輸出的結(jié)果可能不是我們想要的那個(gè)答案,這是因?yàn)槲覀儎?chuàng)建了兩個(gè)線程,這兩個(gè)線程的同時(shí)占用counter這個(gè)資源,前一個(gè)線程剛修改counter的值,后一個(gè)線程可能就會(huì)立即覆蓋掉當(dāng)前這個(gè)counter的值。

接下來(lái)我們對(duì)這個(gè)例子進(jìn)行改造:

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
//共享變量,沒(méi)有互斥鎖或原子操作
int counter = 0;

//定義一個(gè)互斥鎖
mutex mymutex;

//線程函數(shù)  對(duì)counter進(jìn)行自增操作
void increment_counter(int times)
{
    for(int i=0;i<times;++i)
    {
        mymutex.lock();//在訪問(wèn)臨界資源之前先加鎖
        counter++;
        mymutex.unlock();//訪問(wèn)完了之后解鎖,把鎖釋放
    }
}

int main()
{
    //創(chuàng)建兩個(gè)線程,每個(gè)線程對(duì)counter自增100000次
    thread t1(increment_counter,100000);
    thread t2(increment_counter,100000);

    //等待兩個(gè)線程完成
    t1.join();
    t2.join();

    //輸出結(jié)果
    cout<<"最終結(jié)果:"<<counter<<endl;

    return 0;
}

通過(guò)加上互斥鎖之后,我們輸出的結(jié)果將是正確的。

四、loock 和 unlock

在C++中,使用std::mutex的lock()和unlock()函數(shù)來(lái)管理對(duì)共享資源的訪問(wèn),從而確保在多線程環(huán)境中資源的同步訪問(wèn)。

以下是關(guān)于如何使用它們以及需要注意的事項(xiàng):

1.如何使用?

首先,你需要?jiǎng)?chuàng)建一個(gè)std::mutex對(duì)象

std::mutex mymutex;

在訪問(wèn)共享資源之前,使用lock()函數(shù)鎖定互斥量

mymutex.lock();
//訪問(wèn)共享變量
...............

在互斥鎖被鎖定期間,你可以安全的訪問(wèn)共享資源,因?yàn)槠渌噲D鎖定該互斥量的線程將被阻塞。

一旦完成對(duì)共享資源的訪問(wèn),將會(huì)使用unlock()函數(shù)解鎖互斥量。

//完成對(duì)共享資源的訪問(wèn)
mymutex.unlock();

2.注意事項(xiàng)

(1)死鎖:

如果線程在持有互斥量的情況下調(diào)用了一個(gè)阻塞操作(如另一個(gè)互斥量的lock()),并且這個(gè)阻塞操作永遠(yuǎn)不會(huì)完成(因?yàn)槠渌€程持有它需要的資源),那么就會(huì)發(fā)生死鎖,避免死鎖的一種方法就是始終按照相同的順序鎖定互斥量,或者使用更高級(jí)的同步原語(yǔ),如std::lock_guard或std::unique_lock,它們可以自動(dòng)管理鎖的獲取和釋放。

(2)異常安全:

如果在鎖定互斥量之后拋出異常,那么必須確定互斥量被正確解鎖,使用std::lock_guard或std::unique_lock可以自動(dòng)處理這種情況,因?yàn)樗鼈冊(cè)谖鰳?gòu)時(shí)會(huì)釋放鎖。

如下面這個(gè)例子:

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

//全局互斥量
mutex mymutex;

void safe_function()
{
    std::lock_guard<mutex> lock(mymutex);//鎖定互斥量
    //在這里執(zhí)行需要互斥訪問(wèn)的代碼
    //如果拋出異常,lock_guard會(huì)在析構(gòu)時(shí)自動(dòng)解鎖mymutex
    try{
        //模擬一些可能拋出的異常
        if(/*some condition that might cause an exception*/){
            throw std::runtime_error("An error occurred");//使用拋出異常
        }
        //......其他代碼塊
    }
    catch(const std::exception& e){
        //處理異常,但不需要擔(dān)心解鎖,因?yàn)閘ock_guard會(huì)自動(dòng)處理
        std::cerr << "Caught exception:"<<e.what() << '\n';
    }
    //lock_guard離開(kāi)作用域時(shí)自動(dòng)解鎖mymutex

}

int main()
{
    //假設(shè)這里有一些線程調(diào)用safe_function()
    //由于使用了lock_guard,所以wu'lun是否拋出異常,mymutex都會(huì)被正確解鎖
    //........
    
    return 0;
}

(3)不要手動(dòng)解鎖未鎖定的互斥量:

在調(diào)用unlock()之前,必須確保互斥量已經(jīng)被lock()鎖定,否則,該行為是未定義的。

(4)不要多次鎖定同一互斥量:

對(duì)于非遞歸互斥量(如std::mutex),不要再同一線程中多次鎖定它。這會(huì)導(dǎo)致未定義的行為。如果需要遞歸鎖定,請(qǐng)使用std::recursive_mutex。

(5)使用RAII管理鎖

使用RAII(資源獲取即初始化)原則來(lái)管理鎖的生命周期,通過(guò)std::lock_guardhuostd::unique_lock來(lái)確保鎖在不需要時(shí)自動(dòng)釋放。

(6)避免長(zhǎng)時(shí)間持有鎖

盡量縮短持有鎖的時(shí)間,以減少線程之間的爭(zhēng)用,提高程序的并發(fā)性能。

(7)考慮使用更高級(jí)的同步原語(yǔ)

除了std::mutex之外,C++標(biāo)準(zhǔn)庫(kù)還提供了其他更高級(jí)的同步原語(yǔ),如條件變量(std::condition_variable)、讀寫(xiě)鎖(std::shared_mutex)等,它們可以在特定場(chǎng)景下提供更高效的同步機(jī)制。

五、Mutex的四種類(lèi)型

在C++中,特別是從C++11開(kāi)始,std::mutex及其相關(guān)類(lèi)型提供了一系列用于同步多線程訪問(wèn)共享資源的機(jī)制,以下時(shí)std::mutex的四種主要類(lèi)型及其詳細(xì)解釋?zhuān)?/p>

1.std::mutex

  • 這是最基本的互斥量類(lèi)型
  • 它不允許遞歸鎖頂,即同一個(gè)線程不能被多次鎖定同一個(gè)std::mutex,如果嘗試這樣做,程序的行為將是未定義的,通常會(huì)導(dǎo)致死鎖。
  • 它提供了基本的鎖定(lock)和解鎖(unlock)操作
  • 當(dāng)一個(gè)線程鎖定了一個(gè)std::mutex時(shí),任何其他嘗試鎖定該互斥量的線程都將被阻塞,直到原始線程調(diào)用unlock()釋放它。

2.std::recursive_mutex

  • 這是一個(gè)遞歸(或可重入)互斥量
  • 與std::mutex不同,它允許同一線程多次鎖定一個(gè)互斥量。這可以用于需要遞歸訪問(wèn)受保護(hù)資源的場(chǎng)景。
  • 線程在每次鎖定時(shí)都需要對(duì)應(yīng)的解鎖,以確保正確的同步。
  • 如果線程沒(méi)有正確匹配其鎖定和解鎖操作(即解鎖次數(shù)少于鎖定次數(shù)),則其他線程仍然會(huì)被阻塞。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

std::recursive_mutex mtx;

void recursive_function()
{
    mtx.lock();//第一次鎖定
    cout<<"Thread "<<this_thread::get_id()<<" locked mutex"<<endl;

    //遞歸鎖定
    mtx.lock();//同一線程可以多次鎖定
    cout<<"Thread "<<this_thread::get_id()<<" locked mutex again"<<endl;

    mtx.unlock();//解鎖一次
    cout<<"Thread "<<this_thread::get_id()<<" unlocked mutex"<<endl;

    mtx.unlock();//再次解鎖
    cout<<"Thread "<<this_thread::get_id()<<" unlocked mutex again"<<endl;
}

int main()
{
    std::thread t1(recursive_function);

    t1.join();

    return 0;
}

3.std::timed_mutex

  • 這是一個(gè)帶時(shí)限的互斥量
  • 除了提供基本的鎖定和解鎖操作外,它還允許線程嘗試在一定時(shí)間內(nèi)鎖定互斥量。
  • 如果在指定時(shí)間內(nèi)無(wú)法獲取鎖,try_lock_for()或try_lock_until()函數(shù)將返回失敗,而線程則不會(huì)被阻塞。
  • 這對(duì)于實(shí)現(xiàn)有超時(shí)機(jī)制的資源訪問(wèn)非常有用。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

std::timed_mutex mtx;

void timed_lock_function() {
    auto start = std::chrono::high_resolution_clock::now();//高精度時(shí)間

    //嘗試在指定時(shí)間內(nèi)獲取鎖
    if(mtx.try_lock_for(std::chrono::seconds(2)))//加了一個(gè)2秒的等待時(shí)間
    {
        cout<<"Thread"<<this_thread::get_id()<<" get the lock"<<endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));
        mtx.unlock();
        cout<<"Thread"<<this_thread::get_id()<<" release the lock"<<endl;
    }
    else
    {
        cout<<"Thread"<<this_thread::get_id()<<" can't get the lock"<<endl;
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> diff = end-start;
    cout<<"Thread"<<this_thread::get_id()<<" cost "<<diff.count()<<"s"<<endl;
}

int main()
{
    std::thread t1(timed_lock_function);
    std::thread t2(timed_lock_function);

    t1.join();
    t2.join();
    return 0;
}

4.std::recursive_timed_mutex:

  • 這是以惡搞遞歸且?guī)r(shí)限的互斥量
  • 它結(jié)合了std::recursive_mutex和std::timed_mutex的特性
  • 它允許同一線程多次鎖定同一個(gè)互斥量,并提供了帶時(shí)限的鎖定嘗試功能。
  • 這使得線程在需要遞歸訪問(wèn)資源且希望在一定時(shí)間內(nèi)獲取鎖的場(chǎng)景中更加靈活。

在使用這些互斥量類(lèi)型時(shí),需要注意正確的管理鎖定和解鎖操作,以避免死鎖和其他同步問(wèn)題。同時(shí),根據(jù)具體的應(yīng)用場(chǎng)景和需求選擇合適的互斥量類(lèi)型也是非常重要的。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • C語(yǔ)言實(shí)現(xiàn)生日賀卡

    C語(yǔ)言實(shí)現(xiàn)生日賀卡

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)生日賀卡的具體方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • 判斷機(jī)器大小端的兩種實(shí)現(xiàn)方法

    判斷機(jī)器大小端的兩種實(shí)現(xiàn)方法

    第一種方法,思路:利用指針的強(qiáng)制類(lèi)型轉(zhuǎn)換。第二種方法,思路:利用共用體所有數(shù)據(jù)都從同一地址開(kāi)始存儲(chǔ)。
    2013-03-03
  • C++實(shí)現(xiàn)KDTree 附完整代碼

    C++實(shí)現(xiàn)KDTree 附完整代碼

    這篇文章主要介紹了C++實(shí)現(xiàn)KDTree的代碼詳解,包括kdTree概念介紹及分割的作用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-07-07
  • va_list(),va_start(),va_arg(),va_end() 詳細(xì)解析

    va_list(),va_start(),va_arg(),va_end() 詳細(xì)解析

    這些宏定義在stdarg.h中,所以用到可變參數(shù)的程序應(yīng)該包含這個(gè)頭文件.下面我們寫(xiě)一個(gè)簡(jiǎn)單的可變參數(shù)的函數(shù),該函數(shù)至少有一個(gè)整數(shù)參數(shù),第二個(gè)參數(shù)也是整數(shù),是可選的.函數(shù)只是打印這兩個(gè)參數(shù)的值
    2013-09-09
  • C++?std::thread?使用方法

    C++?std::thread?使用方法

    這篇文章主要介紹了C++?std::thread?如何使用,C++中的std::thread類(lèi)提供了一種方便的多線程編程方式,在使用std::thread類(lèi)時(shí),我們需要注意線程間的同步和通信問(wèn)題,以確保多個(gè)線程之間的正確協(xié)同工作需要的朋友可以參考下
    2023-03-03
  • C++中fstream,ifstream及ofstream用法淺析

    C++中fstream,ifstream及ofstream用法淺析

    這篇文章主要介紹了C++中fstream,ifstream及ofstream用法,適合C++初學(xué)者學(xué)習(xí)文件流的操作,需要的朋友可以參考下
    2014-08-08
  • 一文詳解C語(yǔ)言中文件相關(guān)函數(shù)的使用

    一文詳解C語(yǔ)言中文件相關(guān)函數(shù)的使用

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言中文件相關(guān)函數(shù)的使用,可以實(shí)現(xiàn)文件的讀寫(xiě)、打開(kāi)和關(guān)閉。文中通過(guò)示例進(jìn)行了詳細(xì)介紹,需要的可以參考一下
    2022-07-07
  • C++詳細(xì)講解引用類(lèi)型

    C++詳細(xì)講解引用類(lèi)型

    引用指的是對(duì)一個(gè)對(duì)象的引用。那么什么是對(duì)象?在c++中狹義的對(duì)象指的是用類(lèi),結(jié)構(gòu),聯(lián)合等復(fù)雜數(shù)據(jù)類(lèi)型來(lái)聲明的變量,如?MyClass?myclass,CDialog?mydlg,等等
    2022-07-07
  • C++泛型模板約束深入講解

    C++泛型模板約束深入講解

    C/C++ 作為 C# 語(yǔ)言的前置版本,ECMA工業(yè)化編程語(yǔ)言,自然是存在 “泛型模板約束” 的功能的,只是本文不以 C/C++ 20 新語(yǔ)法搞出來(lái)的 “requires” 關(guān)鍵字來(lái)實(shí)現(xiàn),它很難用
    2022-09-09
  • 淺談C++繼承中的名字查找

    淺談C++繼承中的名字查找

    下面小編就為大家?guī)?lái)一篇淺談C++繼承中的名字查找。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01

最新評(píng)論