我正在尝试用c ++ 11编写一个程序,其中运行多个线程,并且在每个循环中主线程将等待每个线程完成。下面的程序是这个概念的测试程序。
显然我在实现中遗漏了一些微不足道的东西,因为看起来我遇到了死锁(并不总是,只是在一些随机运行期间)。
#include <iostream>
#include <stdio.h>
#include <thread>
#include <chrono>
#include <condition_variable>
#include <mutex>
using namespace std;
class Producer
{
public:
Producer(int a_id):
m_id(a_id),
m_ready(false),
m_terminate(false)
{
m_id = a_id;
m_thread = thread(&Producer::run, this);
// ensure thread is available before it is started
this_thread::sleep_for(std::chrono::milliseconds(100));
}
~Producer() {
terminate();
m_thread.join();
}
void start() {
//cout << "start " << m_id << endl;
unique_lock<mutex> runLock(m_muRun);
m_ready = true;
runLock.unlock();
m_cond.notify_all();
}
void wait() {
cout << "wait " << m_id << endl;
unique_lock<decltype(m_muRun)> runLock(m_muRun);
m_cond.wait(runLock, [this]{return !m_ready;});
}
void terminate() {
m_terminate = true;
start();
}
void run() {
do {
unique_lock<decltype(m_muRun)> runLock(m_muRun);
m_cond.wait(runLock, [this]{return m_ready;});
if (!m_terminate) {
cout << "running thread: " << m_id << endl;
} else {
cout << "exit thread: " << m_id << endl;
}
runLock.unlock();
m_ready = false;
m_cond.notify_all();
} while (!m_terminate);
}
private:
int m_id;
bool m_ready;
bool m_terminate;
thread m_thread;
mutex m_muRun;
condition_variable m_cond;
};
int main()
{
Producer producer1(1);
Producer producer2(2);
Producer producer3(3);
for (int i=0; i<10000; ++i) {
cout << i << endl;
producer1.start();
producer2.start();
producer3.start();
producer1.wait();
producer2.wait();
producer3.wait();
}
cout << "exit" << endl;
return 0;
}
发生死锁时程序的输出:
....
.......
running thread: 2
running thread: 1
wait 1
wait 2
wait 3
running thread: 3
查看死锁发生时程序的输出,我怀疑程序的瓶颈是有时在相应的线程实际启动之前调用Producer::wait
函数,即命令Producer::start
应该已经触发了开始,是的解锁互斥锁,但线程的运行方法(Producer::run
)还没有找到它,(注意:我不是100%肯定这个!)。我在这里有点失落,希望有人可以提供一些帮助。
答案 0 :(得分:1)
此代码中有竞争条件:
runLock.unlock();
m_ready = false;
m_ready
变量必须始终受互斥锁保护才能正常同步。并且完全没有必要等待线程启动this_thread::sleep_for()
- 正确的同步也会处理它,所以你可以简单地删除该行。注意这是进行适当多线程的非常低效的方法 - 应该有线程池而不是单独的对象,每个对象都有单独的互斥和条件变量。