【问题标题】:Error on moving a packaged_task object to lambda capture将 packaged_task 对象移动到 lambda 捕获时出错
【发布时间】: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&lt;void()&gt;) ()' _work_queue.emplace_back([task=::std::move(task)]() { task(); }); 抱怨emplace_back 我不明白出了什么问题,因为据我所知packaged_task 只能移动,我正在通过移动捕获任务。

【问题讨论】:

  • 您可以尝试将 lambda 转换为 std::function&lt;void&gt; 吗?

标签: c++ lambda concurrency c++14


【解决方案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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-27
    • 2017-12-17
    • 2020-09-02
    • 2012-01-28
    • 1970-01-01
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    相关资源
    最近更新 更多