能否用std::queue<std::packaged_task<void()>>作为线程池任务队列?
线程池任务队列能否使用
std::queue<std::packaged_task<void()>>? 我在实现线程池的任务提交逻辑时,最初尝试直接将std::packaged_task对象传入lambda并存入std::queue<std::function<void()>>,但因std::function要求可拷贝构造而失败。随后我将任务队列改为std::queue<std::packaged_task<void()>>,代码运行正常,但有观点认为std::packaged_task不可拷贝,队列内部拷贝元素会引发问题,现咨询这种做法是否可行。
修改后的可正常运行的参考代码
using Task = std::function<void()>; std::queue<Task> tasks_; template<typename F, typename... Args> auto ThreadPool::submitTask(F &&f, Args &&... args) -> std::future<decltype(f(args...))> { using RetType = decltype(f(args...)); auto task = std::make_shared<std::packaged_task<RetType()>>( std::bind(std::forward<F>(f), std::forward<Args>(args)...)); std::future<RetType> res = task->get_future(); { std::unique_lock<std::mutex> lock(mutex_); if (!notFullCondition_.wait_for(lock, std::chrono::seconds(1), [&] { return taskSize_.load() < taskQueueThreshold_; })) { std::cerr << "ThreadPool::submitTask() failed" << std::endl; std::packaged_task<RetType()> emptytask([] { return RetType(); }); auto ret = emptytask.get_future(); emptytask(); return ret; } tasks_.emplace([=] { (*task)(); }); ++taskSize_; notEmptyCondition_.notify_all(); } return res; }
尝试改用std::packaged_task作为队列元素的代码
using Task = std::packaged_task<void()>; std::queue<Task> tasks_; template<typename F, typename... Args> auto ThreadPool::submitTask(F &&f, Args &&... args) -> std::future<decltype(f(args...))> { using RetType = decltype(f(args...)); std::packaged_task<RetType()> task(std::bind(std::forward<F>(f), std::forward<Args>(args)...)); std::future<RetType> res = task.get_future(); { std::unique_lock<std::mutex> lock(mutex_); if (!notFullCondition_.wait_for(lock, std::chrono::seconds(1), [&] { return taskSize_.load() < taskQueueThreshold_; })) { std::cerr << "ThreadPool::submitTask() failed" << std::endl; std::packaged_task<RetType()> emptytask([] { return RetType(); }); auto ret = emptytask.get_future(); emptytask(); return ret; } tasks_.emplace([task=std::move(task)] mutable { task(); }); ++taskSize_; notEmptyCondition_.notify_all(); } return res; }
分析与推荐
不推荐使用std::queue<std::packaged_task<void()>>的原因
std::packaged_task本身是不可拷贝的类型,虽然当前代码通过emplace配合移动语义将任务放入队列能正常运行,但如果后续队列操作涉及元素拷贝(比如队列扩展、拷贝队列等场景),会直接触发编译错误或未定义行为,存在潜在的兼容性和维护风险。
推荐方案
- 使用
std::shared_ptr封装std::packaged_task,保留原std::function<void()>的任务队列接口,这种方式更优雅,且从根源避免了拷贝问题; - 将任务队列改为
std::queue<std::shared_ptr<std::packaged_task<void()>>>,直接存储智能指针,通过指针访问任务对象。
内容的提问来源于stack exchange,提问作者牛牪犇
相关产品推荐
相关产品推荐

