我正在尝试构建一个需要由一个线程执行的函数的工作队列,并且可以由许多线程提供。为实现这一目标,我计划使用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的返回类型,我只想执行它,但我可以找出模板的详细信息。
非常感谢任何见解!
答案 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();
}
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
个仿函数。