-
Notifications
You must be signed in to change notification settings - Fork 53
Closed
Labels
Description
start
成员函数:
void start(){
for (std::size_t i = 0; i < num_threads_; ++i){
pool_.emplace_back([this] {
while (!stop_) {
Task task;
{
std::unique_lock<std::mutex> lc{ mutex_ };
if (tasks_.empty())
return;
cv_.wait(lc, [this] {return stop_ || !tasks_.empty(); });
task = std::move(tasks_.front());
tasks_.pop();
}
task();
}
});
}
}
问题在于:
std::unique_lock<std::mutex> lc{ mutex_ };
if (tasks_.empty())
return;
这样写可能导致线程开始执行,任务还没加入,队列为空,就直接退出了。
ThreadPool pool{ 4 }; // 构造函数调用 start 成员函数,任务还未加入,直接退出
// ...
pool.submit(task); // 任务还未加入
有时你可以看到任务正确执行,有时不行。这个问题的原因很简单: pool_.emplace_back
只是构造线程对象,线程对象构造代表启动线程,但是线程何时执行,是系统调度的事情,也和硬件有所关系。
正常的做法我们应该是将判断移动到 cv_.wait
的下面,也就是:
std::unique_lock<std::mutex> lc{ mutex_ };
cv_.wait(lc, [this] {return stop_ || !tasks_.empty(); });
if (tasks_.empty())
return;
join()
成员函数:
void join(){
for (auto& thread : pool_) {
if (thread.joinable()) {
thread.join();
}
}
pool_.clear();
}
但是一旦改成这样,我们的 join() 成员函数则可能出现阻塞,这是因为:
-
假设线程池有三个线程,添加了 2 个任务给线程池执行,线程 1 2 去执行完毕了,任务队列也就空了。
-
然而线程 3 在调用
wait
函数的时候被阻塞了。cv_.wait(lc, [this] {return stop_ || !tasks_.empty(); });
-
我们调用
join()
成员函数,另外两个线程正常的退出,但是线程 3 会被一直阻塞在wait
无法退出,因为条件始终无法满足,stop_
只有在调用stop()
成员函数后才会为true
,而!tasks_.empty()
因为线程队列已经为空,所以始终为false
,将一直阻塞。
修改
放弃 join()
成员函数并修改给出的使用示例,