最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

一、什么是線程同步?

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

為什么要同步呢?

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

以下是一個簡單的例子:

那么我們有沒有什么辦法,控制這些線程對臨界資源的訪問呢?想個什么辦法才能使它們之間不亂套呢?這就該線程同步機制登場了

二、線程同步機制

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

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

三、互斥鎖(Mutex)

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

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

互斥鎖通常具有以下幾個特性:

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

//線程函數(shù)  對counter進行自增操作
void increment_counter(int times)
{
    for(int i=0;i<times;++i)
    {
        //這是一個數(shù)據(jù)競爭,因為多個線程可能同時執(zhí)行這行代碼
        counter++;
    }
}

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

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

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

    return 0;
}

在以上的這個例子中,輸出的結(jié)果可能不是我們想要的那個答案,這是因為我們創(chuàng)建了兩個線程,這兩個線程的同時占用counter這個資源,前一個線程剛修改counter的值,后一個線程可能就會立即覆蓋掉當前這個counter的值。

接下來我們對這個例子進行改造:

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

//定義一個互斥鎖
mutex mymutex;

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

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

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

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

    return 0;
}

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

四、loock 和 unlock

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

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

1.如何使用?

首先,你需要創(chuàng)建一個std::mutex對象

std::mutex mymutex;

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

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

在互斥鎖被鎖定期間,你可以安全的訪問共享資源,因為其他試圖鎖定該互斥量的線程將被阻塞。

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

//完成對共享資源的訪問
mymutex.unlock();

2.注意事項

(1)死鎖:

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

(2)異常安全:

如果在鎖定互斥量之后拋出異常,那么必須確定互斥量被正確解鎖,使用std::lock_guard或std::unique_lock可以自動處理這種情況,因為它們在析構(gòu)時會釋放鎖。

如下面這個例子:

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

//全局互斥量
mutex mymutex;

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

}

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

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

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

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

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

(5)使用RAII管理鎖

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

(6)避免長時間持有鎖

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

(7)考慮使用更高級的同步原語

除了std::mutex之外,C++標準庫還提供了其他更高級的同步原語,如條件變量(std::condition_variable)、讀寫鎖(std::shared_mutex)等,它們可以在特定場景下提供更高效的同步機制。

五、Mutex的四種類型

在C++中,特別是從C++11開始,std::mutex及其相關(guān)類型提供了一系列用于同步多線程訪問共享資源的機制,以下時std::mutex的四種主要類型及其詳細解釋:

1.std::mutex

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

2.std::recursive_mutex

  • 這是一個遞歸(或可重入)互斥量
  • 與std::mutex不同,它允許同一線程多次鎖定一個互斥量。這可以用于需要遞歸訪問受保護資源的場景。
  • 線程在每次鎖定時都需要對應(yīng)的解鎖,以確保正確的同步。
  • 如果線程沒有正確匹配其鎖定和解鎖操作(即解鎖次數(shù)少于鎖定次數(shù)),則其他線程仍然會被阻塞。
#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

  • 這是一個帶時限的互斥量
  • 除了提供基本的鎖定和解鎖操作外,它還允許線程嘗試在一定時間內(nèi)鎖定互斥量。
  • 如果在指定時間內(nèi)無法獲取鎖,try_lock_for()或try_lock_until()函數(shù)將返回失敗,而線程則不會被阻塞。
  • 這對于實現(xià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();//高精度時間

    //嘗試在指定時間內(nèi)獲取鎖
    if(mtx.try_lock_for(std::chrono::seconds(2)))//加了一個2秒的等待時間
    {
        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限的互斥量
  • 它結(jié)合了std::recursive_mutex和std::timed_mutex的特性
  • 它允許同一線程多次鎖定同一個互斥量,并提供了帶時限的鎖定嘗試功能。
  • 這使得線程在需要遞歸訪問資源且希望在一定時間內(nèi)獲取鎖的場景中更加靈活。

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

總結(jié)

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

相關(guān)文章

  • 基于VC中使用ForceInclude來強制包含stdafx.h的解決方法

    基于VC中使用ForceInclude來強制包含stdafx.h的解決方法

    本篇文章是對VC中使用ForceInclude來強制包含stdafx.h的解決方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C/C++通過SQLite SDK實現(xiàn)數(shù)據(jù)庫增刪改查操作

    C/C++通過SQLite SDK實現(xiàn)數(shù)據(jù)庫增刪改查操作

    SQLite,作為一款嵌入式關(guān)系型數(shù)據(jù)庫管理系統(tǒng),一直以其輕量級、零配置以及跨平臺等特性而備受青睞,本文主要介紹了C++如何通過SQLite SDK實現(xiàn)數(shù)據(jù)庫增刪改查操作,感興趣的可以了解下
    2023-11-11
  • 使用C語言中的time函數(shù)獲取系統(tǒng)時間

    使用C語言中的time函數(shù)獲取系統(tǒng)時間

    在C語言中可以使用time函數(shù)來獲取系統(tǒng)時間,以下對time函數(shù)進行了介紹,需要的朋友可以過來參考下
    2013-07-07
  • linux之sed命令的用法

    linux之sed命令的用法

    sed是一個很好的文件處理工具,本身是一個管道命令,主要是以行為單位進行處理,可以將數(shù)據(jù)行進行替換、刪除、新增、選取等特定工作,下面先了解一下sed的用法
    2013-10-10
  • VC2022中“編譯器的堆空間不足”的解決辦法

    VC2022中“編譯器的堆空間不足”的解決辦法

    本文主要介紹了VS2022編譯C++程序時出現(xiàn)"編譯器堆空間不足"錯誤的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-10-10
  • C++ Array容器的顯示和隱式實例化詳細介紹

    C++ Array容器的顯示和隱式實例化詳細介紹

    這篇文章主要介紹了C++中Array容器的隱式實例化和顯式實例化,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2022-10-10
  • C++ std:map的使用方法

    C++ std:map的使用方法

    std::map是C++標準庫中一個強大而高效的關(guān)聯(lián)容器,本文就來介紹一下C++ std:map的使用方法,具有一定的參考價值,感興趣的可以了解一下
    2025-02-02
  • C++設(shè)計一個簡單內(nèi)存池的全過程

    C++設(shè)計一個簡單內(nèi)存池的全過程

    利用C/C++開發(fā)大型應(yīng)用程序中,內(nèi)存的管理與分配是一個需要認真考慮的部分,下面這篇文章主要給大家介紹了關(guān)于C++設(shè)計一個簡單內(nèi)存池的全過程,需要的朋友可以參考下
    2021-09-09
  • c++讀寫文件流實例程序講解

    c++讀寫文件流實例程序講解

    這篇文章主要介紹了c++讀寫文件流實例,大家參考使用吧
    2013-12-12
  • 利用Matlab復刻兩款粒子愛心動畫效果

    利用Matlab復刻兩款粒子愛心動畫效果

    最近電視劇《點燃我,溫暖你》大火,蹭一下熱度,發(fā)一下MATLAB畫愛心的代碼,寫的比較隨意,大家可以自行調(diào)整粒子大小和顏色啥的
    2022-11-11

最新評論

桦南县| 青田县| 隆林| 农安县| 巴南区| 六枝特区| 来宾市| 青田县| 阜新| 阳山县| 达尔| 金山区| 凤凰县| 阜平县| 荆州市| 睢宁县| 商都县| 青海省| 鄂托克旗| 吕梁市| 绥棱县| 越西县| 读书| 扶余县| 乾安县| 上虞市| 南投县| 莎车县| 海林市| 石楼县| 周宁县| 林甸县| 茌平县| 瑞昌市| 毕节市| 鄂伦春自治旗| 固原市| 门头沟区| 曲沃县| 彭阳县| 永州市|