C++ 互斥锁不起作用 - 同步失败

问题描述

我想应用尽可能简单的互斥锁。

#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) sum 总是等于 300
b) counter1 总是 100
c) counter2 始终为 0

怎么了?

编辑:

当我调试 sum 条件中的 else 变量时,我看到如下值: 200、400、100,甚至是 0(我认为加法甚至没有发生)。

解决方法

C++ 互斥锁不起作用 - 同步失败

为什么第一次学习这些东西的每个人都假设适用于其他人的久经考验的同步原语已经失效,而不是他们的假设?

互斥锁很好。你的心智模式被打破了。这应该是您的开始假设。

我希望:

  1. sum 总是等于 300

如果您在检查值之前df = pd.DataFrame({'credit': {0: 0.0,1: 0.0,2: 0.0,3: 500.0,4: 0.0},'debit': {0: 89.4,1: 4.33,2: 12.0,3: 0.0,4: 5.4}}) 处理了两个线程,就会出现这种情况。但是您还没有这样做,因此您正在对 join 进行完全不同步的读取,而另外两个线程可能正在对其进行变异。这是一场数据竞赛。除非您在访问数据时总是使用互斥锁,否则互斥锁不会保护您的数据。

假设我们进行了最小的更改,因此 sum 始终受到保护:

sum

现在一些可用的排序是:

  1. abc - 您的预期(以及如果您在阅读 thread t1(addValue,100); // a thread t2(addValue,200); // b m.lock(); if (sum == 300) // c { counter1++; } else { counter2++; } sum = 0; m.unlock(); 之前加入两个线程会保证什么)
  2. acb - 您在第 sum 行读取 100,增加 c,第二个线程将 counter2 增加到 sum after 你读过它(但你从来没有看到过)
  3. cab - 在两个线程被安排运行之前,您立即读取 300
  4. bca - 你读了 0,它在你检查后增加到 200
  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,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;
}

如果您想要我的另一个示例来测量线程等待的时间check this one