当我试图通过另一个线程唤醒一个线程时遇到了一个问题。一个简单的生产者/消费者。
代码下方。第 85 行是我不明白为什么它不起作用的地方。生产者线程填充 std::queue 并调用 std::condition_variable.notify_one() 而消费者线程正在等待 NOT std::queue.empty()。
在此先感谢您的帮助
#include <mutex>
#include <condition_variable>
#include <queue>
#include <string>
#include <iostream>
#include <thread>
// request
class request :
public std::mutex,
public std::condition_variable,
public std::queue<std::string>
{
public:
virtual ~request();
};
request::~request()
{
}
// producer
class producer
{
public:
producer(request &);
virtual ~producer();
void operator()();
private:
request & request_;
};
producer::producer(request & _request)
:
request_(_request)
{
}
producer::~producer()
{
}
void
producer::operator()()
{
while (true) {
std::lock_guard<std::mutex> lock(request_);
std::cout << "producer\n";
request_.push("something");
std::this_thread::sleep_for(std::chrono::seconds(1));
request_.notify_one();
}
}
class consumer
{
public:
consumer(request &);
virtual ~consumer();
void operator()();
private:
request & request_;
};
consumer::consumer(request & _request)
:
request_(_request)
{
}
consumer::~consumer()
{
}
void
consumer::operator()()
{
while (true) {
std::unique_lock<std::mutex> lock(request_); // <-- the problem
std::cout << "consumer\n";
request_.wait (
lock, [this] {return !request_.empty();}
);
request_.pop();
}
}
int
main()
{
// request
request request_;
// producer
std::thread producer_{producer(request_)};
// consumer
std::thread first_consumer_{consumer(request_)};
std::thread second_consumer_{consumer(request_)};
// join
producer_.join();
first_consumer_.join();
second_consumer_.join();
}
最佳答案
修复了以下代码,并进行了以下更改:
- 不要像那样从 mutex、condvar 和 queue 派生,那太可怕了。
- 将项目添加到队列后尽快解锁互斥锁,临界区应始终尽可能小。这允许消费者在生产者 sleep 时醒来。
- Flush
cout
(我使用endl
来做到这一点)所以输出会立即打印出来,这样可以更容易地看到发生了什么。 - 打印
“consumer”
after 醒来,因为那是消费者消费的时候,否则你会得到误导性的输出,显示消费者何时 sleep ,而不是何时开始工作做。
您的代码的主要问题是生产者从未给消费者运行的机会。它添加到队列中,休眠一秒钟(仍然持有互斥锁)然后通知条件变量(仍然持有互斥锁),然后很快释放互斥锁并再次获取它。可能你看到的是消费者线程收到通知,试图获取互斥锁,发现它仍然被锁定(由生产者线程),所以又回到 sleep 状态。生产者从未释放足够长的互斥量以供另一个线程获取它。您可能已经能够通过在生产者循环开始时添加 std::this_thread::yield()
来获得更好的结果,之前 锁定互斥量,但是依赖于 yield()
来确保正确性的算法通常会被破坏(事实上,这在我的测试中没有任何区别);最好修复生产者循环,让消费者有机会醒来并运行。
这是工作代码:
#include <mutex>
#include <condition_variable>
#include <queue>
#include <string>
#include <iostream>
#include <thread>
// request
struct request
{
std::mutex mx;
std::condition_variable cv;
std::queue<std::string> q;
};
// producer
class producer
{
public:
producer(request & r) : request_(r) { }
void operator()();
private:
request & request_;
};
void
producer::operator()()
{
while (true) {
{
std::lock_guard<std::mutex> lock(request_.mx);
std::cout << "producer" << std::endl;
request_.q.push("something");
}
std::this_thread::sleep_for(std::chrono::seconds(1));
request_.cv.notify_one();
}
}
class consumer
{
public:
consumer(request & r) : request_(r) { }
void operator()();
private:
request & request_;
};
void
consumer::operator()()
{
while (true) {
std::unique_lock<std::mutex> lock(request_.mx);
request_.cv.wait (
lock, [this] {return !request_.q.empty();}
);
std::cout << "consumer" << std::endl;
request_.q.pop();
}
}
int
main()
{
// request
request request_;
// producer
std::thread producer_{producer(request_)};
// consumer
std::thread first_consumer_{consumer(request_)};
std::thread second_consumer_{consumer(request_)};
// join
producer_.join();
first_consumer_.join();
second_consumer_.join();
}
关于C++11 - 无法使用 std::thread 和 std::condition_variable 唤醒线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16613512/