我希望尽可能应用简单的互斥。
#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++互斥锁不工作-同步失败
为什么每个第一次学习这些东西的人都假设那些为其他人工作的经过验证的同步原语被破坏了,而不是他们的假设?
互斥锁没有问题。你的思维模式坏了。这应该是你的起始假设。
我期望:
- 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();
现在一些可用的排序是:
- abc -你所期望的(以及如果你在读取
sum
之前连接两个线程将保证什么) - acb -你在
c
行读取100
,增加counter2
,第二个线程在之后将sum
增加到300
你读取它(但你从来没有看到过这个) - cab -在两个线程被安排运行 之前,您立即读取
- bca -你读
200
,它后来增加到300
后,你检查 - 等。
0
。每一个排列都是允许的,除非你做了一些努力来显式排列它们
它正在按预期工作,问题是您没有预料到"时间"对于所有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;
}
如果你想要另一个测量线程等待时间的例子,请检查这个