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

C++中線程同步與互斥的四種方式介紹及對比詳解

 更新時間:2025年01月14日 10:06:26   作者:碼事漫談  
在C++中,當兩個或更多的線程需要訪問共享數(shù)據(jù)時,就會出現(xiàn)線程安全問題,這是因為,如果沒有適當?shù)耐綑C制,一個線程可能在另一個線程還沒有完成對數(shù)據(jù)的修改就開始訪問數(shù)據(jù),這將導致數(shù)據(jù)的不一致性和程序的不可預測性,本文介紹了C++中線程同步與互斥的四種方式介紹及對比

引言

在C++中,當兩個或更多的線程需要訪問共享數(shù)據(jù)時,就會出現(xiàn)線程安全問題。這是因為,如果沒有適當?shù)耐綑C制,一個線程可能在另一個線程還沒有完成對數(shù)據(jù)的修改就開始訪問數(shù)據(jù),這將導致數(shù)據(jù)的不一致性和程序的不可預測性。為了解決這個問題,C++提供了多種線程同步和互斥的機制。

1. 互斥量(Mutex)

互斥量是一種同步機制,用于防止多個線程同時訪問共享資源。在C++中,可以使用std::mutex類來創(chuàng)建互斥量。

#include <thread>
#include <mutex>

std::mutex mtx;  // 全局互斥量
int shared_data = 0;  // 共享數(shù)據(jù)

void thread_func() {
    for (int i = 0; i < 10000; ++i) {
        mtx.lock();  // 獲取互斥量的所有權(quán)
        ++shared_data;  // 修改共享數(shù)據(jù)
        mtx.unlock();  // 釋放互斥量的所有權(quán)
    }
}

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

    t1.join();
    t2.join();

    std::cout << shared_data << std::endl;  // 輸出20000

    return 0;
}

在上述代碼中,我們創(chuàng)建了一個全局互斥量mtx和一個共享數(shù)據(jù)shared_data。然后,我們在thread_func函數(shù)中使用mtx.lock()和mtx.unlock()來保護對shared_data的訪問,確保在任何時候只有一個線程可以修改shared_data。

2. 鎖(Lock)

除了直接使用互斥量,C++還提供了std::lock_guard和std::unique_lock兩種鎖,用于自動管理互斥量的所有權(quán)。

#include <thread>
#include <mutex>

std::mutex mtx;  // 全局互斥量
int shared_data = 0;  // 共享數(shù)據(jù)

void thread_func() {
    for (int i = 0; i < 10000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);  // 創(chuàng)建鎖,自動獲取互斥量的所有權(quán)
        ++shared_data;  // 修改共享數(shù)據(jù)
        // 鎖在離開作用域時自動釋放互斥量的所有權(quán)
    }
}

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

    t1.join();
    t2.join();

    std::cout << shared_data << std::endl;  // 輸出20000

    return 0;
}

在上述代碼中,我們使用std::lock_guard來自動管理互斥量的所有權(quán)。當創(chuàng)建std::lock_guard對象時,它會自動獲取互斥量的所有權(quán),當std::lock_guard對象離開作用域時,它會自動釋放互斥量的所有權(quán)。這樣,我們就不需要手動調(diào)用mtx.lock()和mtx.unlock(),可以避免因忘記釋放互斥量而導致的死鎖。

3. 條件變量(Condition Variable)

條件變量是一種同步機制,用于在多個線程之間同步條件的變化。在C++中,可以使用std::condition_variable類來創(chuàng)建條件變量。

#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;  // 全局互斥量
std::condition_variable cv;  // 全局條件變量
bool ready = false;  // 共享條件

void print_id(int id) {
    std::unique_lock<std::mutex> lock(mtx);  // 創(chuàng)建鎖,自動獲取互斥量的所有權(quán)
    while (!ready) {  // 如果條件不滿足
        cv.wait(lock);  // 等待條件變量的通知
    }
    // 當收到條件變量的通知,且條件滿足時,繼續(xù)執(zhí)行
    std::cout << "thread " << id << '\n';
}

void go() {
    std::unique_lock<std::mutex> lock(mtx);  // 創(chuàng)建鎖,自動獲取互斥量的所有權(quán)
    ready = true;  // 修改共享條件
    cv.notify_all();  // 通知所有等待的線程
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i)
        threads[i] = std::thread(print_id, i);

    std::cout << "10 threads ready to race...\n";
    go();  // 開始比賽

    for (auto& th : threads) th.join();

    return 0;
}

在上述代碼中,我們創(chuàng)建了一個全局互斥量mtx、一個全局條件變量cv和一個共享條件ready。然后,我們在print_id函數(shù)中使用cv.wait(lock)來等待條件變量的通知,當收到條件變量的通知,且條件滿足時,繼續(xù)執(zhí)行。在go函數(shù)中,我們修改共享條件,并使用cv.notify_all()來通知所有等待的線程。

4. 原子操作(Atomic Operation)

原子操作是一種特殊的操作,它可以在多線程環(huán)境中安全地對數(shù)據(jù)進行讀寫,而無需使用互斥量或鎖。在C++中,可以使用std::atomic模板類來創(chuàng)建原子類型。

#include <thread>
#include <atomic>

std::atomic<int> shared_data(0);  // 共享數(shù)據(jù)

void thread_func() {
    for (int i = 0; i < 10000; ++i) {
        ++shared_data;  // 原子操作
    }
}

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

    t1.join();
    t2.join();

    std::cout << shared_data << std::endl;  // 輸出20000

    return 0;
}

在上述代碼中,我們創(chuàng)建了一個原子類型的共享數(shù)據(jù)shared_data。然后,我們在thread_func函數(shù)中使用++shared_data來進行原子操作,這樣,我們就不需要使用互斥量或鎖,也可以保證在任何時候只有一個線程可以修改shared_data。

5. 對比

策略優(yōu)點缺點
單一全局互斥量簡單可能導致嚴重的性能問題,降低并發(fā)性
多個互斥量提高并發(fā)性增加程序復雜性,需要避免死鎖
原子操作提高并發(fā)性,避免互斥量開銷增加程序復雜性,需要理解和使用原子操作
讀寫鎖提高并發(fā)性,特別是讀操作多于寫操作時增加程序復雜性,需要管理讀寫鎖,需要避免死鎖

案例舉例

假設(shè)我們正在開發(fā)一個在線聊天 服務(wù)器,需要處理大量的并發(fā)連接。每個連接都有一個關(guān)聯(lián)的用戶對象,用戶對象包含了用戶的狀態(tài)信息,如用戶名、在線狀態(tài)等。

在這種情況下,我們可以使用多個互斥量的策略。我們可以將用戶對象劃分為幾個組,每個組有一個關(guān)聯(lián)的互斥量。當一個線程需要訪問一個用戶對象時,它只需要鎖定該用戶對象所在組的互斥量,而不是所有的用戶對象。這樣,不同的線程可以同時訪問不同的用戶對象,從而提高并發(fā)性。

同時,我們也可以使用讀寫鎖的策略。因為在大多數(shù)情況下,線程只需要讀取用戶的狀態(tài)信息,而不需要修改。所以,我們可以使用讀寫鎖,允許多個線程同時讀取用戶對象,但在修改用戶對象時需要獨占鎖。

在實踐中,我們可能需要結(jié)合使用這兩種策略,以達到最佳的效果。

6. 更進一步:原子操作+鎖

原子操作和鎖是兩種不同的線程同步機制,它們可以單獨使用,也可以一起使用,具體取決于你的應(yīng)用場景。

原子操作是一種低級的同步機制,它可以保證對單個內(nèi)存位置的讀寫操作是原子的,即在任何時候只有一個線程可以對內(nèi)存位置進行操作。原子操作通常用于實現(xiàn)高級的同步機制,如鎖和條件變量。

鎖是一種高級的同步機制,它可以保證對一段代碼或多個內(nèi)存位置的訪問是原子的,即在任何時候只有一個線程可以執(zhí)行被鎖保護的代碼或訪問被鎖保護的內(nèi)存位置。

如果你在使用鎖的同時還使用原子操作,那么你需要確保你的代碼正確地理解和使用這兩種同步機制。例如,如果你在一個被鎖保護的代碼段中使用原子操作,那么你需要確保原子操作不會違反鎖的語義,即在任何時候只有一個線程可以執(zhí)行被鎖保護的代碼。

以下是一個使用原子操作和鎖的例子:

#include <thread>
#include <mutex>
#include <atomic>

std::mutex mtx;  // 全局互斥量
std::atomic<int> counter(0);  // 原子計數(shù)器

void thread_func() {
    for (int i = 0; i < 10000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);  // 獲取互斥量的所有權(quán)
        ++counter;  // 原子操作
        // 鎖在離開作用域時自動釋放互斥量的所有權(quán)
    }
}

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

    t1.join();
    t2.join();

    std::cout << counter << std::endl;  // 輸出20000

    return 0;
}

在上述代碼中,我們使用std::lock_guard來獲取互斥量的所有權(quán),然后使用++counter來進行原子操作。這樣,我們既保證了在任何時候只有一個線程可以執(zhí)行被鎖保護的代碼,也保證了對counter的操作是原子的。

總的來說,原子操作和鎖可以一起使用,但你需要確保你的代碼正確地理解和使用這兩種同步機制。

總結(jié)

在C++中,當兩個或更多的線程需要訪問共享數(shù)據(jù)時,可以使用互斥量、鎖、條件變量和原子操作等多種線程同步和互斥的機制來保證線程安全。選擇哪種機制,取決于具體的應(yīng)用場景和需求。

以上就是C++中線程同步與互斥的四種方式介紹及對比詳解的詳細內(nèi)容,更多關(guān)于C++線程同步與互斥方式的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++淺析內(nèi)存分區(qū)模型概念與示例

    C++淺析內(nèi)存分區(qū)模型概念與示例

    在了解內(nèi)存分區(qū)之前,我們先來聊一聊為什么要進行內(nèi)存分區(qū)。在進行了內(nèi)存分區(qū)之后,在不同的區(qū)域存放的數(shù)據(jù),會有不同的生命周期,從而會讓程序員的編程變得更加靈活
    2022-09-09
  • C語言可變參數(shù)與內(nèi)存管理超詳細講解

    C語言可變參數(shù)與內(nèi)存管理超詳細講解

    有時,您可能會碰到這樣的情況,您希望函數(shù)帶有可變數(shù)量的參數(shù),而不是預定義數(shù)量的參數(shù)。C 語言為這種情況提供了一個解決方案,這篇文章主要介紹了C語言可變參數(shù)與內(nèi)存管理
    2023-01-01
  • 一篇文章帶你了解C語言的一些重要字符串與內(nèi)存函數(shù)

    一篇文章帶你了解C語言的一些重要字符串與內(nèi)存函數(shù)

    這篇文章主要介紹了C語言字符函數(shù)、內(nèi)存函數(shù) 功能,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • C語言順序表的基本操作(初始化,插入,刪除,查詢,擴容,打印,清空等)

    C語言順序表的基本操作(初始化,插入,刪除,查詢,擴容,打印,清空等)

    這篇文章主要介紹了C語言順序表的基本操作(初始化,插入,刪除,查詢,擴容,打印,清空等),具有很好的參考價值,希望對大家有所幫助。
    2023-02-02
  • C++日期和時間編程小結(jié)

    C++日期和時間編程小結(jié)

    這篇文章主要介紹了C++日期和時間編程小結(jié)的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • C++中繼承基類與派生類的區(qū)別

    C++中繼承基類與派生類的區(qū)別

    這篇文章主要介紹了C++中繼承基類與派生類的區(qū)別,面向?qū)ο蟪绦蛟O(shè)計中最重要的一個概念是繼承。繼承允許我們依據(jù)另一個類來定義一個類,這使得創(chuàng)建和維護一個應(yīng)用程序變得更容易,需要的朋友可以參考下
    2023-05-05
  • C++將二叉樹轉(zhuǎn)為雙向鏈表及判斷兩個鏈表是否相交

    C++將二叉樹轉(zhuǎn)為雙向鏈表及判斷兩個鏈表是否相交

    這篇文章主要介紹了C++將二叉樹轉(zhuǎn)為雙向鏈表及判斷兩個鏈表是否相交的方法,文中還給出了求兩個鏈表相交的第一個節(jié)點列的實現(xiàn)方法,需要的朋友可以參考下
    2016-02-02
  • 詳解C++中的指針、數(shù)組指針與函數(shù)指針

    詳解C++中的指針、數(shù)組指針與函數(shù)指針

    本文從初學者的角度,深入淺出地講解C++中的指針、數(shù)組指針與函數(shù)指針,對最常混淆的引用傳遞、值傳遞和指針傳遞做了區(qū)處,需要的朋友可以參考下
    2015-07-07
  • 從頭學習C語言之二維數(shù)組

    從頭學習C語言之二維數(shù)組

    這篇文章主要為大家詳細介紹了C語言之二維數(shù)組,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • 一文帶你深入了解C++中的類型轉(zhuǎn)換

    一文帶你深入了解C++中的類型轉(zhuǎn)換

    在C語言中,如果賦值運算符左右兩側(cè)類型不同,或者形參與實參類型不匹配,或者返回值類型與接收返回值類型不一致時,就需要發(fā)生類型轉(zhuǎn)化。本文主要介紹了C++中常見的四個類型轉(zhuǎn)換,需要的可以參考一下
    2022-12-12

最新評論