未来转换的模式

时间:2012-10-25 10:32:37

标签: c++ boost boost-thread

目前我们非常重视异步值。 假设我有一个类似这样的函数:

int do_something(const boost::posix_time::time_duration& sleep_time)
{
    BOOST_MESSAGE("Sleeping a bit");
    boost::this_thread::sleep(sleep_time);
    BOOST_MESSAGE("Finished taking a nap");

    return 42;
}

在代码中的某个时刻,我们创建了一个任务,它创建了一个这样的int值的未来,这个值将由packaged_task设置 - 就像这样(在这个例子中,worker_queue是一个boost :: asio :: io_service):

boost::unique_future<int> createAsynchronousValue(const boost::posix_time::seconds& sleep)
{
    boost::shared_ptr< boost::packaged_task<int> > task(
        new boost::packaged_task<int>(boost::bind(do_something, sleep)));
    boost::unique_future<int> ret = task->get_future();

    // Trigger execution
    working_queue.post(boost::bind(&boost::packaged_task<int>::operator (), task));

    return boost::move(ret);
}

在代码的另一点,我想包装这个函数来返回一个更高级别的对象,这个对象也应该是未来。我需要一个转换函数,它接受第一个值并将其转换为另一个值(在我们的实际代码中,我们有一些分层并执行异步RPC,将期货返回到响应 - 这些响应应转换为期货到真实对象,POD甚至无效未来能够等待它或捕获异常)。所以这是本例中的转换函数:

float converter(boost::shared_future<int> value)
{
    BOOST_MESSAGE("Converting value " << value.get());
    return 1.0f * value.get();
}

然后我考虑创建一个懒惰的未来,如Boost文档中所述,仅在需要时进行此转换:

void invoke_lazy_task(boost::packaged_task<float>& task)
{
    try
    {
        task();
    }
    catch(boost::task_already_started&)
    {}
}

然后我有一个函数(可能是更高级别的API)来创建一个包装的未来:

boost::unique_future<float> createWrappedFuture(const boost::posix_time::seconds& sleep)
{
    boost::shared_future<int> int_future(createAsynchronousValue(sleep));
    BOOST_MESSAGE("Creating converter task");
    boost::packaged_task<float> wrapper(boost::bind(converter, int_future));

    BOOST_MESSAGE("Setting wait callback");
    wrapper.set_wait_callback(invoke_lazy_task);

    BOOST_MESSAGE("Creating future to converter task");
    boost::unique_future<float> future = wrapper.get_future();

    BOOST_MESSAGE("Returning the future");
    return boost::move(future);
}

最后我希望能够像这样使用它:

{    
    boost::unique_future<float> future = createWrappedFuture(boost::posix_time::seconds(1));
    BOOST_MESSAGE("Waiting for the future");
    future.wait();
    BOOST_CHECK_EQUAL(future.get(), 42.0f);
}

但在这里,我最终得到了一个关于破坏承诺的例外。原因似乎对我来说非常清楚,因为执行转换的packaged_task超出了范围。

所以我的任务是:我如何处理这种情况。如何防止任务被破坏?有这种模式吗?

贝斯茨,

罗尼

1 个答案:

答案 0 :(得分:0)

您需要正确管理任务对象的生命周期。

最正确的方法是从boost::packaged_task<float>返回boost::unique_future<float>而不是createWrappedFuture()。调用者将负责获取未来的对象并延长任务的生命周期,直到未来的值准备就绪。

或者您可以将任务对象放入某个“待处理”队列(全局或类成员),方法与createAsynchronousValue中的方式类似。但在这种情况下,您将需要explcitly管理任务生命周期并在完成后将其从队列中删除。所以不要认为这个解决方案有利于返回任务对象本身。