将 packaged_task 对象移动到 lambda 捕获时出错
Posted
技术标签:
【中文标题】将 packaged_task 对象移动到 lambda 捕获时出错【英文标题】:Error on moving a packaged_task object to lambda capture 【发布时间】:2016-08-25 19:55:46 【问题描述】:我正在实现一个线程池,它在可调用对象上有一个push_back
方法。但是,我在使用 lambda 技巧将打包任务移动到函数对象时遇到错误。
class Threadpool
public:
// ...
::std::deque <::std::function<void()>> _work_queue;
::std::mutex _work_queue_mutex;
::std::condition_variable _worker_signal;
template <typename CallableT>
::std::future<::std::result_of_t<CallableT()>> push_back(CallableT&&);
template<typename CallableT>
::std::future<::std::result_of_t<CallableT()>> Threadpool::push_back(CallableT&& callable)
::std::packaged_task<::std::result_of_t<CallableT()>()> task (::std::move(callable));
auto fu = task.get_future();
::std::unique_lock<::std::mutex> locker(_work_queue_mutex);
// COMPILE ERROR
_work_queue.emplace_back([task=::std::move(task)] () task(); )
_worker_signal.notify_one();
return fu;
Threadpool pool;
pool.emplace_back( []() ::std::cout << "hello\n"; );
编译器通过error: no match for call to '(const std::packaged_task<void()>) ()' _work_queue.emplace_back([task=::std::move(task)]() task(); );
抱怨emplace_back
我不明白出了什么问题,因为据我所知packaged_task
只能移动,我正在通过移动捕获任务。
【问题讨论】:
您可以尝试将 lambda 转换为std::function<void>
吗?
【参考方案1】:
你的例子有两个问题。
确实,std::packaged_task
只能移动,所以[task=std::move(task)]
是正确的。但除此之外,std::packaged_task::operator()
需要 not-const
对象:https://en.cppreference.com/w/cpp/thread/packaged_task/operator()
因此必须将 lambda 定义为 mutable
以允许使用 task()
:
[task=std::move(task)] () mutable task(); ;
但即便如此,lambda 对象也只能移动而不能复制,而std::function
需要一个可复制对象:https://en.cppreference.com/w/cpp/utility/functional/function
因此,其中一种解决方案是将packaged_task
包装在一个可复制的智能指针中,如下所示:
#include <mutex>
#include <deque>
#include <functional>
#include <condition_variable>
#include <future>
#include <iostream>
#include <type_traits>
class Threadpool
public:
// ...
std::deque <std::function<void()>> _work_queue;
std::mutex _work_queue_mutex;
std::condition_variable _worker_signal;
template <typename CallableT>
std::future<std::result_of_t<CallableT()>> push_back(CallableT&&);
;
template<typename CallableT>
std::future<std::result_of_t<CallableT()>> Threadpool::push_back(CallableT&& callable)
auto task = std::make_shared<std::packaged_task<std::result_of_t<CallableT()>()>>( std::move(callable) );
auto fu = task->get_future();
std::unique_lock<std::mutex> locker(_work_queue_mutex);
_work_queue.emplace_back([task]() (*task)(); );
_worker_signal.notify_one();
return fu;
;
int main()
Threadpool pool;
pool.push_back( []() std::cout << "hello\n"; );
演示:https://gcc.godbolt.org/z/aEfvo7Mhz
【讨论】:
以上是关于将 packaged_task 对象移动到 lambda 捕获时出错的主要内容,如果未能解决你的问题,请参考以下文章