You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用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,提问作者牛牪犇

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 08:02:43