c++ - 竞争条件 2 个线程交替
问题描述
所以我希望程序输出 1\n2\n1\n2\n1\n2\n 但它似乎卡在某个地方。但是当我调试它并在声明 t2 后立即在 cv1.notify_one() 处设置一个断点时,它会执行吗?
#include <iostream>
#include <mutex>
#include <thread>
#include <condition_variable>
using namespace std;
mutex cout_lock;
condition_variable cv1, cv2;
mutex mtx1;
unique_lock<std::mutex> lck1(mtx1);
mutex mtx2;
unique_lock<std::mutex> lck2(mtx2);
const int COUNT = 3;
int main(int argc, char** argv)
{
thread t1([&](){
for(int i = 0; i < COUNT; ++i)
{
cv1.wait(lck1);
cout << "1" << endl;
cv2.notify_one();
}
});
thread t2([&](){
for(int i = 0; i < COUNT; ++i)
{
cv2.wait(lck2);
cout << "2" << endl;
cv1.notify_one();
}
});
cv1.notify_one();
t1.join();
t2.join();
return 0;
}
解决方案
我认为您的线程开始和调用 cv1.notify_one();
in之间存在数据竞争main()
。
考虑cv1.notify_one()
在线程 1 开始并调用之前调用发生的情况cv1.wait()
。之后没有人再打电话cv1.notify
了,你的简历就在等着。这称为丢失的唤醒。
您需要一种机制在 main 中等待,直到两个线程都启动,然后执行cv1.notify()
下面是使用 int 和互斥锁的示例。
#include "pch.h"
#include <iostream>
#include <mutex>
#include <thread>
#include <condition_variable>
using namespace std;
condition_variable cv1, cv2;
mutex m;
const int COUNT = 3;
enum Turn
{
T1,
T2
};
int main(int argc, char** argv)
{
mutex thread_start_mutex;
int num_started_threads = 0;
Turn turn = T1;
thread t1([&]() {
{
// increase the number of started threads
unique_lock<std::mutex> lck(thread_start_mutex);
++num_started_threads;
}
for (int i = 0; i < COUNT; ++i)
{
// locked cout, unlock before calling notify
{
unique_lock<std::mutex> lck1(m);
// wait till main thread calls notify
cv1.wait(lck1, [&] { return turn == T1;});
cout << "1 a really long string" << endl;
turn = T2; // next it's T2's turn
}
cv2.notify_one();
}
});
thread t2([&]() {
{
// increase the number of started threads
unique_lock<std::mutex> lck(thread_start_mutex);
++num_started_threads;
}
for (int i = 0; i < COUNT; ++i)
{
// locked cout, unlock before calling notify
{
unique_lock<std::mutex> lck2(m);
cv2.wait(lck2, [&] {return turn == T2;});
cout << "2 some other stuff to test" << endl;
turn = T1;
}
cv1.notify_one();
}
});
unique_lock<std::mutex> lck(thread_start_mutex);
// wait until both threads have started
cv1.wait(lck, [&] { return num_started_threads == 2; });
lck.unlock();
cv1.notify_one();
t1.join();
t2.join();
return 0;
}
此外,还不清楚为什么您有两个互斥锁被锁定在 main 之外。我通常认为互斥锁是一种受保护的资源,不应同时访问。似乎这个想法是为了保护 cout 调用,您应该为此使用一个互斥锁,每个线程将锁定、执行 cout、解锁并通知另一个。
编辑
我的原始答案在调用 t1.notify() 和 t2.wait() 之间存在完全相同的问题。如果 t1.notify() 在线程 2 等待之前被调用,线程 2 永远不会被唤醒。
为了解决这个问题,我添加了一个枚举“Turn”,它指示轮到谁了,每个等待条件现在检查是否轮到他们。如果是,他们不会等待而只是打印出来,所以即使错过了通知,他们仍然会完成他们的任务。如果不是轮到他们,他们将阻塞,直到其他线程设置轮到变量并调用通知。
注意:这展示了一个很好的示例/实践,即在使用 cv.wait() 时有条件通常要好得多。这既使意图清晰,又避免了丢失的唤醒和虚假的唤醒。
注意 2这个解决方案可能过于复杂,一般来说条件变量和互斥锁不太可能是解决这个问题的最佳方法。
推荐阅读
- node.js - node.js“使用不推荐使用的字段:'series'”同时使用c3图表制作器
- html - 多次点击 Bootstrap 下拉菜单
- java - Java中jsp显示列表的问题
- javascript - 以编程方式从 iCloud 提取备份消息和/或已安装的应用程序列表
- javascript - Jquery中的就绪函数不提供输出
- python - Heroku 没有在 Ruby/Python 应用程序中安装 requirements.txt 内容
- python - Python BigQuery 错误:HTTPSConnectionPool(host='bigquery.googleapis.com', port=443):读取超时。(读取超时=60)
- angular - NgFor 仅支持绑定到 Iterables,例如 Arrays。角度错误,无法将 GET API 用于前端 HTML
- firebase - 我可以在 Firebase 应用服务器中同时创建的最大应用实例数是多少
- vue.js - WebpackChunkName 使用当前工作目录而不是 web 根目录