簡體   English   中英

C++ 互斥鎖不起作用 - 同步失敗

[英]C++ mutex doesn't work - synchronization fails

我想應用盡可能簡單的互斥鎖。

#include <iostream>
#include <thread>
#include <vector>
#include <functional>
#include <algorithm>
#include <mutex>
using namespace std;

int sum;
static mutex m;

void addValue(int value)
{
    m.lock();
    sum += value;
    m.unlock();
}

int main()
{

    int counter1 = 0;
    int counter2 = 0;
    for (int i = 0; i < 100; i++)
    {
        thread t1(addValue, 100);
        thread t2(addValue, 200);

        if (sum == 300)
        {
            counter1++;
        }
        else
        {
            counter2++;
        }
        sum = 0;
        t1.join();
        t2.join();
    }
    cout << counter1 << endl;
    cout << counter2 << endl;
}

不幸的是,上面提到的代碼沒有按預期工作。 我希望:

a) 總和總是等於 300
b) counter1 始終為 100
c) counter2 始終為 0

怎么了?

編輯:

當我在else條件下調試sum變量時,我看到如下值:200、400、100 甚至 0(我假設加法甚至沒有發生)。

C++ 互斥鎖不起作用 - 同步失敗

為什么第一次學習這些東西的每個人都假設適用於其他所有人的久經考驗的同步原語被破壞了,而不是他們的假設?

互斥體很好。 你的精神 model 壞了。 這應該是你的開始假設。

我希望:

  1. 總和始終等於 300

如果您在檢查值之前join兩個線程,就會出現這種情況。 但是您還沒有這樣做,因此您正在對sum進行完全非同步的讀取,而另外兩個線程可能正在對其進行變異。 這是一場數據競賽。 除非您在訪問數據時始終使用互斥鎖,否則互斥鎖不會保護您的數據。

假設我們進行了最小的更改,因此sum始終受到保護:

    thread t1(addValue, 100); // a
    thread t2(addValue, 200); // b

    m.lock();
    if (sum == 300)           // c
    {
        counter1++;
    }
    else
    {
        counter2++;
    }
    sum = 0;
    m.unlock();

現在一些可用的訂單是:

  1. abc - 您的預期(以及如果您在閱讀sum之前加入兩個線程可以保證什么)
  2. acb - 您在c行讀取100 ,遞增counter2 ,第二個線程在讀取sum遞增到300 (但您從未見過)
  3. cab - 在兩個線程甚至被安排運行之前,您立即讀取0
  4. bca - 你讀到200 ,后來你檢查后增加到300
  5. 等等 -每個排列都是允許的,除非你努力明確地對它們進行排序

它按預期工作,問題是您沒想到所有 3 個線程的“時間”都不相同,並且您忽略了一個線程先於另一個線程開始的明顯事情,這顯然增加了一個優勢,甚至更多如果它只需要循環 100 次增量。

#include <iostream>
#include <thread>
#include <mutex>

bool keep_alive;

void add_value_mutex(std::mutex * mx, int * trg, int value) {
    while (keep_alive){
        mx->lock();
        (*trg) += value;
        mx->unlock();
    }
}

int main(){

    std::thread thread_1;
    std::thread thread_2;

    int count_targ = 2000;
    int * counter_1 = new int(0);
    int * counter_2 = new int(0);

    /* --- */

    std::mutex mx_1;
    std::mutex mx_2;

    keep_alive = true;
    thread_1 = std::thread(add_value_mutex, &mx_1, counter_1, 1);
    thread_2 = std::thread(add_value_mutex, &mx_2, counter_2, 1);

    while(1){

        if (mx_1.try_lock()){
            if (count_targ <= * counter_1){
                mx_1.unlock();
                break;
            }
            mx_1.unlock();
        }

        if (mx_2.try_lock()){
            if (count_targ <= * counter_2){
                mx_2.unlock();
                break;
            }
            mx_2.unlock();
        }

    }
    
    keep_alive = false;
    thread_1.join();
    thread_2.join();
        
    std::cout << "Thread 1 (independent mutex) -> " << * counter_1 << std::endl;
    std::cout << "Thread 2 (independent mutex) -> " << * counter_2 << std::endl;

    /* --- */

    keep_alive = true;
    (*counter_1) = 0;
    (*counter_2) = 0;

    std::mutex mx_s;
    thread_1 = std::thread(add_value_mutex, &mx_s, counter_1, 1);
    thread_2 = std::thread(add_value_mutex, &mx_s, counter_2, 1);

    while(1){

        if (mx_s.try_lock()){
            if (count_targ <= * counter_1 || count_targ <= * counter_2){
                mx_s.unlock();
                break;
            }
            mx_s.unlock();
        }
    }

    std::cout << "Thread 1 (shared mutex) -> " << * counter_1 << std::endl;
    std::cout << "Thread 2 (shared mutex) -> " << * counter_2 << std::endl;

    keep_alive = false;
    thread_1.join();
    thread_2.join();


    delete counter_1;
    delete counter_2;

    return 0;
}

如果你想要我的另一個例子來測量線程等待的時間,請檢查這個

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM