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

怎么了?

编辑:

当我在else条件下调试sum变量时,我看到如下值:200,400,100,甚至0(我假设加法根本没有发生过)。

c++互斥锁不工作-同步失败

为什么每个第一次学习这些东西的人都假设那些为其他人工作的经过验证的同步原语被破坏了,而不是他们的假设?

互斥锁没有问题。你的思维模式坏了。这应该是你的起始假设。

我期望:

  1. sum总是等于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 -在两个线程被安排运行
  4. 之前,您立即读取0
  5. bca -你读200,它后来增加到300后,你检查
  6. 等。

每一个排列都是允许的,除非你做了一些努力来显式排列它们

它正在按预期工作,问题是您没有预料到"时间"对于所有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;
}

如果你想要另一个测量线程等待时间的例子,请检查这个