我正在尝试构建一个需要由一个线程执行并且可以由多个线程提供的函数的工作队列。为了实现这一目标,我计划使用 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<...>每次都不会一样。具体来说,它的返回值将会改变(但是,函数永远不会采用任何参数)。因此,我必须有一个看起来像这样的 add 函数:
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 的返回类型,我只想执行它,但我可以弄清楚模板详细信息。
任何见解将不胜感激!
I'm trying to construct a work queue of functions that need to be executed by one thread and can be fed by many threads. To accomplish this, I was planning on using the boost::packaged_task and boost::unique_future. The idea would be you would do:
Foo value = queue.add(myFunc).get();
which would block, until the function is executed. So queue.add(...) takes in a boost::function, and returns a boost::unique_future. Internally it then creates a boost::packaged_task using the boost::function for its constructor.
The problem I'm running into is that boost::function<...> won't be the same every time. Specifically, the return value for it will change (the functions, however, will never take any parameters). Thus, I have to have an add function that looks something like:
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();
}
Okay, that doesn't seem too bad, but then I ran into the problem of how to define 'queue'. I think I have no choice but to use boost::any, since the types will not be constant:
std::list<boost::any> queue; // note: I'm not concerned with thread-safety yet
But then I run into a problem when I try to implement my executeSingle (takes just a single item off the queue to execute):
void executeSingle() {
boost::any value = queue.back();
boost::packaged_task<?> task = boost::packaged_task<?>(boost::move(value));
// actually execute task
task();
queue.pop_back();
}
The '?' denote what I'm unsure about. I can't call executeSingle with a template, as it's called from a separate thread. I tried using boost::any, but I get the error:
conversion from 'boost::any' to non-scalar type boost::detail::thread_move_t<boost:thread>' requested.
The funny part is, I actually don't care about the return type of packaged_task at this point, I just want to execute it, but I can figure out the template details.
Any insight would be greatly appreciated!
发布评论
评论(3)
您应该存储
boost::function
的。请注意,boost::packaged_task::operator()
不会返回任何内容;它填充关联的boost::future
。事实上,即使它返回了一些东西,你仍然可以使用boost::function
因为你仍然对返回的值不感兴趣:你所关心的只是调用 <代码>queue.back()()。如果是这种情况,boost::function::operator()
会为您丢弃返回的值。需要注意的是,您可能需要更改
add
方法的签名,以在泛型类型Functor
而不是boost::function,并使用
boost::result_of
获取boost::packaged_task
的结果类型。我的整体建议:
编辑
如何处理
queue::add
内的移动语义,其中
dereference_functor
可能是:您也可以替换
bind
表达式更加清晰,也不需要自定义函子。但是,如果
task_type::operator()
有多个重载,则可能需要消除歧义;如果 Boost.Thread 中的未来更改引入重载,代码也可能会中断。You should store
boost::function<void()>
's. Note thatboost::packaged_task<R>::operator()
doesn't return anything; it populates the associatedboost::future
. In fact, even if it returned something you could still useboost::function<void()>
since you'd still have no interest in the returned value: all you care about is to callqueue.back()()
. If this were the caseboost::function<void()>::operator()
would take care of discarding the returned value for you.As a minor note, you might want to change the signature of your
add
method to be templated on a generic typeFunctor
rather than aboost::function
, and useboost::result_of
to get the result type forboost::packaged_task
.My suggestion as a whole:
EDIT
How to take care of move-semantics inside
queue::add
where
dereference_functor
could be:You could also substitute the
bind
expression for the much clearerwhich also doesn't require a custom functor. However if there are multiple overloads of
task_type::operator()
this might need disambiguation; the code could also break if a future change in the Boost.Thread introduce an overload.您使用老式的虚拟函数。使用
virtual
execute
方法定义基类task_base
,然后定义一个保存特定任务实例的模板派生类。大致意思是:并定义您的队列来保存
unique_ptr
。这本质上就是boost::any
的作用,只是您会使用特定的函数,即execute
。注意:未经测试的代码!而且我仍然不太熟悉右值引用。这只是为了让您了解代码的外观。
You use old-fashioned virtual functions. Define a base class
task_base
with avirtual
execute
method, then define a template derived class which holds a specific task instance. Something along the lines:And define your queue to hold
unique_ptr<task_base>
. This is essentially whatboost::any
does, only you'd be using a specific function, namelyexecute
.NOTE: Untested code! And I'm still not very familiar with rvalue references. This is just to give you the idea of how the code would look.
虽然有点晚了,但您可能需要考虑使用 Boost.Asio,而不是滚动自己的队列运行器解决方案。
虽然它是作为一个 I/O 库发展起来的,但它确实支持像这样的异步调用。只需在某处定义一个 io_service ,在线程内运行它,然后在该线程上调用 post 函子即可。
Somewhat belatedly, but you might want to consider using Boost.Asio instead of rolling your own queue-runner solution.
While this grew up as an I/O library it does support asynchronous calls just like this. Simply define an
io_service
somewhere, run it inside a thread, and thenpost
functors to get called on that thread.