如何创建一个包含boost :: packaged_task<>的队列具有返回任意类型的函数?

时间:2011-05-04 20:36:00

标签: c++ templates boost queue packaged-task

我正在尝试构建一个需要由一个线程执行的函数的工作队列,并且可以由许多线程提供。为实现这一目标,我计划使用boost :: packaged_task和boost :: unique_future。这个想法是你会做的:

Foo value = queue.add(myFunc).get();

会阻止,直到执行该功能。所以queue.add(...)接受一个boost :: function,并返回一个boost :: unique_future。然后在内部使用boost :: function为其构造函数创建一个boost :: packaged_task。

我遇到的问题是boost :: function< ...>每次都不一样。具体来说,它的返回值将会改变(但是,函数永远不会采用任何参数)。因此,我必须有一个类似于:

的添加函数
template <typename ResultType>
boost::unique_future<ResultType> add(boost::function<ResultType ()> f) {
   boost::packaged_task<boost::function<ResultType ()> > task(f);
   queue.push_back(task);
   return task.get_future();
}

好吧,这似乎并不太糟糕,但后来我遇到了如何定义'队列'的问题。我想我别无选择,只能使用boost :: any,因为类型不会是常数:

std::list<boost::any> queue; // note: I'm not concerned with thread-safety yet

但是当我尝试实现我的executeSingle时,我遇到了一个问题(只从队列中取出一个项目来执行):

void executeSingle() {
    boost::any value = queue.back();
    boost::packaged_task<?> task = boost::packaged_task<?>(boost::move(value));
    // actually execute task
    task();
    queue.pop_back();
}

'?'表示我不确定的事情。我不能用模板调用executeSingle,因为它是从一个单独的线程调用的。我尝试使用boost :: any,但是我收到了错误:

  conversion from 'boost::any' to non-scalar type  boost::detail::thread_move_t<boost:thread>' requested.

有趣的是,我实际上并不关心packaged_task的返回类型,我只想执行它,但我可以找出模板的详细信息。

非常感谢任何见解!

3 个答案:

答案 0 :(得分:5)

你应该存储boost::function<void()>。请注意boost::packaged_task<R>::operator()不会返回任何内容;它填充了相关的boost::future。事实上,即使它返回了某些东西,你仍然可以使用boost::function<void()>,因为你仍然对返回的值没有兴趣:你所关心的只是调用queue.back()()。如果是这种情况,boost::function<void()>::operator()将负责为您丢弃返回的值。

作为次要注释,您可能希望更改add方法的签名,以便在通用类型Functor而不是boost::function上进行模板化,并使用{{1}获取boost::result_of的结果类型。

我的整体建议:

boost::packaged_task

修改

如何处理template<typename Functor> boost::future<typename boost::result_of<Functor()>::type> queue::add(Functor functor) // assuming your class is named queue { typedef typename boost::result_of<Functor()>::type result_type; boost::packaged_task<result_type> task(functor); boost::unique_future<result_type> future = task.get_future(); internal_queue.push_back(boost::move(task)); // assuming internal_queue member return boost::move(future); } void queue::executeSingle() { // Note: do you really want LIFO here? queue.back()(); queue.pop_back(); }

中的move-semantics
queue::add

typedef typename boost::result_of<Functor()>::type result_type; typedef boost::packaged_task<result_type> task_type; boost::shared_ptr<task_type> task = boost::make_shared<task_type>(functor); boost::unique_future<result_type> future = task->get_future(); /* boost::shared_ptr is possibly move-enabled so you can try moving it */ internal_queue.push_back( boost::bind(dereference_functor(), task) ); return boost::move(future); 可以是:

dereference_functor

您也可以将struct dereference_functor { template<typename Pointer> void operator()(Pointer const& p) const { (*p)(); } }; 表达式替换为更清晰的

bind

也不需要自定义仿函数。但是,如果boost::bind(&task_type::operator(), task) 存在多个重载,则可能需要消除歧义;如果Boost.Thread中的未来变化引入了过载,代码也可能会中断。

答案 1 :(得分:1)

您使用老式的虚拟功能。使用task_base virtual方法定义基类execute,然后定义包含特定任务实例的模板派生类。一些事情:

struct task_base {
  virtual void execute() = 0;
};
template<typename ResultType>
struct task_holder : task_base {
  task_holder(boost::packaged_task<boost::function<ResultType ()> >&& task)
    : m_task(task) { }
  void execute() {
    m_task();
  }
private:
  boost::packaged_task<boost::function<ResultType ()> > m_task;
};

并定义您的队列以容纳unique_ptr<task_base>。这基本上是boost::any所做的,只有你使用的是特定的函数,即execute

注意:未经测试的代码!而且我对rvalue引用仍然不是很熟悉。这只是为了让您了解代码的外观。

答案 2 :(得分:0)

有点迟了,但你可能想考虑使用Boost.Asio而不是滚动你自己的队列运行器解决方案。

虽然这是一个I / O库,它确实支持异步调用。只需在某处定义一个io_service,在一个线程中运行它,然后在该线程上调用post个仿函数。