【发布时间】:2016-06-02 22:26:07
【问题描述】:
我在使用这个线程池类时遇到了卡在我的 c++ 程序中:
class ThreadPool {
unsigned threadCount;
std::vector<std::thread> threads;
std::list<std::function<void(void)> > queue;
std::atomic_int jobs_left;
std::atomic_bool bailout;
std::atomic_bool finished;
std::condition_variable job_available_var;
std::condition_variable wait_var;
std::mutex wait_mutex;
std::mutex queue_mutex;
std::mutex mtx;
void Task() {
while (!bailout) {
next_job()();
--jobs_left;
wait_var.notify_one();
}
}
std::function<void(void)> next_job() {
std::function<void(void)> res;
std::unique_lock<std::mutex> job_lock(queue_mutex);
// Wait for a job if we don't have any.
job_available_var.wait(job_lock, [this]()->bool { return queue.size() || bailout; });
// Get job from the queue
mtx.lock();
if (!bailout) {
res = queue.front();
queue.pop_front();
}else {
// If we're bailing out, 'inject' a job into the queue to keep jobs_left accurate.
res = [] {};
++jobs_left;
}
mtx.unlock();
return res;
}
public:
ThreadPool(int c)
: threadCount(c)
, threads(threadCount)
, jobs_left(0)
, bailout(false)
, finished(false)
{
for (unsigned i = 0; i < threadCount; ++i)
threads[i] = std::move(std::thread([this, i] { this->Task(); }));
}
~ThreadPool() {
JoinAll();
}
void AddJob(std::function<void(void)> job) {
std::lock_guard<std::mutex> lock(queue_mutex);
queue.emplace_back(job);
++jobs_left;
job_available_var.notify_one();
}
void JoinAll(bool WaitForAll = true) {
if (!finished) {
if (WaitForAll) {
WaitAll();
}
// note that we're done, and wake up any thread that's
// waiting for a new job
bailout = true;
job_available_var.notify_all();
for (auto& x : threads)
if (x.joinable())
x.join();
finished = true;
}
}
void WaitAll() {
std::unique_lock<std::mutex> lk(wait_mutex);
if (jobs_left > 0) {
wait_var.wait(lk, [this] { return this->jobs_left == 0; });
}
lk.unlock();
}
};
gdb 说(停止阻塞执行时)卡在 (std::unique_lock&, ThreadPool::WaitAll()::{lambda()#1})+58>
我正在使用支持 c++14 (-std=c++1y) 的 g++ v5.3.0
如何避免这个问题?
更新
我已经编辑(重写)了课程:https://github.com/edoz90/threadpool/blob/master/ThreadPool.h
【问题讨论】:
-
我相当肯定 std::atomic_bool 上的操作符不是原子操作。唯一有效的原子操作是标准的“原子操作库”部分中指定的一组 atomic_foo() 方法。因此,无互斥量的减量+notify_one() 可能没有与互斥量 lock+wait() 正确排序。我有点守旧。我还没有使用所有这些新的、花哨的 atomic-this-es 和 atomic-that-es。我使用互斥保护变量的老式方法,并且仅在持有互斥锁时通知。
-
wait_mutex的目的是什么? -
@SamVarshavchik all member functions of
atomicare atomic 并且默认内存顺序是memory_order_seq_cst -
operator--在原子变量 is 上是原子的(等效于fetch_add (-1))。这里的问题是在工作线程和调用AddJob和JoinAll的线程之间存在jobs_left的竞争条件。 IMO,您应该摆脱该变量并直接使用作业队列,使用互斥体保护它,包括在JoinAll内部,并测试它的大小。 -
首先您等待
job_left为零-然后您提高bailout-然后由于bailout而将新作业添加到队列中-非常复杂-您为什么要这样做?finished的目的是什么?而且您不需要在JoinAll中使用queue_mutex吗?一个非常简单的类的非常复杂的代码。
标签: c++ multithreading threadpool mutex