4

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

Foo 值 = 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 的返回类型,我只想执行它,但我可以弄清楚模板的详细信息。

任何见解将不胜感激!

4

3 回答 3

5

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

作为一个小提示,您可能希望将add方法的签名更改为在泛型类型Functor而不是 a上进行模板化boost::function,并用于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)

这也不需要自定义函子。但是,如果有多个重载task_type::operator()this 可能需要消除歧义;如果 Boost.Thread 中的未来更改引入了重载,代码也可能会中断。

于 2011-05-05T04:05:48.023 回答
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.

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

于 2011-05-04T21:06:57.737 回答
0

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 then post functors to get called on that thread.

于 2013-09-06T01:14:33.460 回答