[英]std::condition_variable – notify once but wait thread wakened twice
Here's a simple C++ thread pool implementation. 这是一个简单的C ++线程池实现。 It's an altered version orginated from https://github.com/progschj/ThreadPool .
它是一个修改过的版本,来自https://github.com/progschj/ThreadPool 。
#ifndef __THREAD_POOL_H__
#define __THREAD_POOL_H__
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
namespace ThreadPool {
class FixedThreadPool {
public:
FixedThreadPool(size_t);
template<class F, class... Args>
auto Submit(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type>;
template<class F, class... Args>
void Execute(F&& f, Args&&... args);
~FixedThreadPool();
void AwaitTermination();
void Stop();
private:
void ThreadWorker();
// need to keep track of threads so we can join them
std::vector<std::thread> workers;
// the task queue
std::queue< std::function<void()> > tasks;
// synchronization
std::mutex worker_mutex;
std::mutex queue_mutex;
std::condition_variable condition;
// stop flag
bool stop_;
// thread size
int thread_size_;
};
// Constructor does nothing. Threads are created when new task submitted.
FixedThreadPool::FixedThreadPool(size_t num_threads):
stop_(false),
thread_size_(num_threads) {}
// Destructor joins all threads
FixedThreadPool::~FixedThreadPool() {
//std::this_thread::sleep_for(std::chrono::seconds(5));
for(std::thread &worker: workers) {
if (worker.joinable()) {
worker.join();
}
}
}
// Thread worker
void FixedThreadPool::ThreadWorker() {
std::function<void()> task;
while (1) {
{
std::unique_lock<std::mutex> lock(this->queue_mutex);
this->condition.wait(lock,
[this]() { return this->stop_ || !this->tasks.empty(); });
printf("wakeeeeeened\n");
if (this->stop_ && this->tasks.empty()) {
printf("returning ...\n");
return;
}
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
}
// Add new work item to the pool
template<class F, class... Args>
auto FixedThreadPool::Submit(F&& f, Args&&... args)
-> std::future<typename std::result_of<F(Args...)>::type >
{
{
std::unique_lock<std::mutex> lock(this->worker_mutex);
if (workers.size() < thread_size_) {
workers.emplace_back(std::thread(&FixedThreadPool::ThreadWorker, this));
}
}
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared< std::packaged_task<return_type()> >(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> res = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
if(stop_) {
throw std::runtime_error("ThreadPool has been shutdown.");
}
tasks.emplace([task]() { (*task)(); });
}
condition.notify_one();
return res;
}
// Execute new task without returning std::future object.
template<class F, class... Args>
void FixedThreadPool::Execute(F&& f, Args&&... args) {
Submit(std::forward<F>(f), std::forward<Args>(args)...);
}
// Blocks and wait for all previously submitted tasks to be completed.
void FixedThreadPool::AwaitTermination() {
for(std::thread &worker: workers) {
if (worker.joinable()) {
worker.join();
}
}
}
// Shut down the threadpool. This method does not wait for previously submitted
// tasks to be completed.
void FixedThreadPool::Stop() {
printf("Stopping ...\n");
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop_ = true;
}
}
} // namespace ThreadPool
#endif /* __THREAD_POOL_H__ */
and the test main.cpp: 和测试main.cpp:
#include <iostream>
#include <vector>
#include <chrono>
#include <exception>
#include "ThreadPool.h"
int main(int argc, char** argv) {
ThreadPool::FixedThreadPool pool(3);
pool.Execute([]() {
std::cout << "hello world" << std::endl;
}
);
pool.Stop();
pool.AwaitTermination();
std::cout << "All tasks complted." << std::endl;
return 0;
}
I have a bug in this test program. 我在这个测试程序中有一个错误。 Only one task is submitted to threadpool, but I got working thread being wakened up twice:
只有一个任务提交给线程池,但我得到了两次唤醒的工作线程:
>>./test
Stopping ...
wakeeeeeened
hello world
wakeeeeeened
returning ...
All tasks complted.
I think the problem is in FixedThreadPool::ThreadWorker() itself. 我认为问题出在FixedThreadPool :: ThreadWorker()本身。 The working thread continuously wait on a conditional variable to get new tasks.
工作线程持续等待条件变量来获取新任务。 The function FixedThreadPool::Submit() adds a new task to queue and call condition.nofity_one() to waken a working thread.
函数FixedThreadPool :: Submit()将一个新任务添加到队列并调用condition.nofity_one()来唤醒一个工作线程。
But I can't figure out how the working thread can be wakened twice. 但我无法弄清楚工作线程如何被唤醒两次。 I have only one task submitted and exactly one working thread in this test.
我只提交了一个任务,并且在此测试中只有一个工作线程。
Converting comments into answer: 将评论转换为答案:
condition_variable::wait(lock, pred)
is equivalent to while(!pred()) wait(lock);
condition_variable::wait(lock, pred)
等同于while(!pred()) wait(lock);
. 。 If
pred()
returns true
then no wait actually takes place and the call returns immediately. 如果
pred()
返回true
则实际上不会发生等待,并且调用立即返回。
Your first wake is from the notify_one()
call; 您的第一个唤醒来自
notify_one()
调用; the second "wake" is because the second wait()
call happens to execute after the Stop()
call, so your predicate returns true
and wait()
returns immediately without waiting. 第二个“唤醒”是因为第二个
wait()
调用恰好在Stop()
调用之后执行,所以你的谓词返回true
并且wait()
立即返回而不等待。
It should be obvious that you got (un)lucky here: if the second wait()
call took place before the Stop()
call, then your worker thread will be stuck waiting forever (in the absence of spurious wakeups), and so will your main thread. 很明显你在这里得到了(不)幸运:如果第二次
wait()
调用发生在Stop()
调用之前,那么你的工作线程将永远等待(没有虚假的唤醒),所以你的主线程。
Also, get rid of __THREAD_POOL_H__
. 另外,摆脱
__THREAD_POOL_H__
。 Burn those double underscores to the ground. 将那些双下划线烧到地上。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.