Multithreading C++;11条件变量
Multithreading C++;11条件变量,multithreading,c++11,condition-variable,Multithreading,C++11,Condition Variable,我试图在C++11中学习并发性时犯很多错误。我不得不问这个, 下面是这个人应该做的: 一个队列,三个线程,一个是假设将一个整数放入队列,另外两个是假设通过弹出队列相应地增加s1,s2,这样我就可以得到队列中的总数。为了简化,我将1到10个数字放入队列中。 但有时它是有效的,有时似乎有一个无限循环::原因是什么? #包括 #包括 #包括 #包括 #包括 #包括 #包括 类线程安全队列{ 私人: 可变std::互斥mut; std::队列数据\队列; std::条件变量数据条件; std::st
我试图在C++11中学习并发性时犯很多错误。我不得不问这个,
下面是这个人应该做的: 一个队列,三个线程,一个是假设将一个整数放入队列,另外两个是假设通过弹出队列相应地增加s1,s2,这样我就可以得到队列中的总数。为了简化,我将1到10个数字放入队列中。
但有时它是有效的,有时似乎有一个无限循环::原因是什么?
#包括
#包括
#包括
#包括
#包括
#包括
#包括
类线程安全队列{
私人:
可变std::互斥mut;
std::队列数据\队列;
std::条件变量数据条件;
std::string log;//看看后面发生了什么
布尔多;
公众:
线程安全队列(){
log=“初始化队列\n”;
完成=错误;
}
线程安全队列(线程安全队列常量和其他){
std::锁紧装置lk(其他。mut);
数据队列=其他。数据队列;
}
无效设置完成(布尔常数s){
标准:锁紧装置lk(mut);
完成=s;
}
完成{
标准:锁紧装置lk(mut);
已完成的返回;
}
无效推送(int新值){
标准:锁紧装置lk(mut);
日志+=“+推送”+std::到字符串(新值)+“\n”;
数据队列推送(新值);
数据条件通知单();
}
无效等待和弹出(int和value){
std::唯一锁lk(mut);
data_cond.wait(lk,[this]{return!data_queue.empty();});
value=data_queue.front();
log+=“-poping”+std::to_字符串(值)+“\n”;
data_queue.pop();
}
std::shared_ptr wait_和_pop(){
std::唯一锁lk(mut);
data_cond.wait(lk,[this]{return!data_queue.empty();});
std::shared_ptr res(std::make_shared(data_queue.front());
log+=“-popping”+std::to_string(*res)+“\n”;
data_queue.pop();
返回res;
}
bool try_pop(int和value){
标准:锁紧装置lk(mut);
if(data_queue.empty()){
log+=“试图弹出,但为空\n”;
返回false;
}
value=data_queue.front();
log+=“-popping”+std::to_字符串(值)+“\n”;
data_queue.pop();
返回true;
}
std::shared_ptr try_pop(){
标准:锁紧装置lk(mut);
if(data_queue.empty()){
log+=“试图弹出,但为空\n”;
返回std::shared_ptr();
}
std::shared_ptr res(std::make_shared(data_queue.front());
log+=“-popping”+std::to_string(*res)+“\n”;
data_queue.pop();
返回res;
}
bool empty()常量{
标准:锁紧装置lk(mut);
//log+=“检查队列是否为空\n”;
返回数据_queue.empty();
}
std::string get_log(){
返回日志;
}
};
线程安全队列tq;
int s1,s2;
无效准备(){
对于(int i=1;i请查看函数p1第5行
if(tq.get_done()&&tq.empty())中断
所以你检查了队列是否为空。它不是。现在你循环并输入
tq.等待和弹出(数据)
你会在哪里找到
data_cond.wait(lk,[this]{return!data_queue.empty();})
这基本上是
while(data_queue.empty()){
等待(lk);
}
请注意缺少的“!”
现在,您的线程坐在那里等待队列不为空,这永远不会发生,因为生产者id已填充队列。线程将永远不会加入
有很多方法可以解决这个问题。我相信你会自己找到一种。在设置done
时,你可能需要通知data\u cond
,并在data\u cond.wait
条件中检查它。我认为如果在push()
之前调用wait\u pop()
,以及push(),就会出现死锁
无法通过互斥锁调用notify\u one
将wait\u和\u pop()
从其等待中释放出来。我认为在获取互斥锁之前,您应该wait()
。@KenY-N,是的,这是我所怀疑的。但是我如何确保push()应该先工作吗?@Nano就像我说的,试着交换std::unique_lock lk(mut);
和data_cond.wait(lk,[this]{return!data_queue.empty()});
。这可以确保在推送某个东西之前代码不会弹出。(我想,这都是我的想法!),你可能想要data_cond.wait(lk,[this]{std::unique_lock lk(mut);return!data_queue.empty()})
,或者重新设计您的整个代码!@KenY-N不,您不能交换这些行,它们的顺序是正确的。请阅读std::condition_variable::wait
如何工作。啊,是的,这也是正确的。如果p2
清空p1
之间的队列tq.empty()
和下一个tq。等等,\u和_pop(data);
会有另一个挂起。@Nano:如果我的答案满足你的问题,考虑在我的答案旁边加上绿色的小勾号。ty
#include <queue>
#include <memory>
#include <mutex>
#include <thread>
#include <iostream>
#include <condition_variable>
#include <string>
class threadsafe_queue {
private:
mutable std::mutex mut;
std::queue<int> data_queue;
std::condition_variable data_cond;
std::string log; //just to see what is going on behind
bool done;
public:
threadsafe_queue(){
log = "initializing queue\n";
done = false;
}
threadsafe_queue(threadsafe_queue const& other) {
std::lock_guard<std::mutex> lk(other.mut);
data_queue = other.data_queue;
}
void set_done(bool const s) {
std::lock_guard<std::mutex> lk(mut);
done = s;
}
bool get_done() {
std::lock_guard<std::mutex> lk(mut);
return done;
}
void push(int new_value) {
std::lock_guard<std::mutex> lk(mut);
log += "+pushing " + std::to_string(new_value) + "\n";
data_queue.push(new_value);
data_cond.notify_one();
}
void wait_and_pop(int& value) {
std::unique_lock<std::mutex> lk(mut);
data_cond.wait(lk, [this]{return !data_queue.empty();});
value = data_queue.front();
log += "-poping " + std::to_string(value) + "\n";
data_queue.pop();
}
std::shared_ptr<int> wait_and_pop() {
std::unique_lock<std::mutex> lk(mut);
data_cond.wait(lk, [this]{return !data_queue.empty();});
std::shared_ptr<int> res(std::make_shared<int>(data_queue.front()));
log += "- popping " + std::to_string(*res) + "\n";
data_queue.pop();
return res;
}
bool try_pop(int& value) {
std::lock_guard<std::mutex> lk(mut);
if (data_queue.empty()) {
log += "tried to pop but it was empty\n";
return false;
}
value = data_queue.front();
log += "-popping " + std::to_string(value) + "\n";
data_queue.pop();
return true;
}
std::shared_ptr<int> try_pop() {
std::lock_guard<std::mutex> lk(mut);
if (data_queue.empty()) {
log += "tried to pop but it was empty\n";
return std::shared_ptr<int>();
}
std::shared_ptr<int> res(std::make_shared<int>(data_queue.front()));
log += "-popping " + std::to_string(*res) + "\n";
data_queue.pop();
return res;
}
bool empty() const {
std::lock_guard<std::mutex> lk(mut);
//log += "checking the queue if it is empty\n";
return data_queue.empty();
}
std::string get_log() {
return log;
}
};
threadsafe_queue tq;
int s1, s2;
void prepare() {
for (int i = 1; i <= 10; i++)
tq.push(i);
tq.set_done(true);
}
void p1() {
while (true) {
int data;
tq.wait_and_pop(data);
s1 += data;
if (tq.get_done() && tq.empty()) break;
}
}
void p2() {
while (true) {
int data;
tq.wait_and_pop(data);
s2 += data;
if (tq.get_done() && tq.empty()) break;
}
}
int main(int argc, char *argv[]) {
std::thread pp(prepare);
std::thread worker(p1);
std::thread worker2(p2);
pp.join();
worker.join();
worker2.join();
std::cout << tq.get_log() << std::endl;
std::cout << s1 << " " << s2 << std::endl;
return 0;
}