0

目前我们正在大量使用异步值。假设我有一个执行以下操作的函数:

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 甚至 void未来能够等待它或捕获异常)。所以这是本例中的转换函数:

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 超出了范围。

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

最好的,

罗尼

4

1 回答 1

0

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

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

或者您可以像在createAsynchronousValue. 但在这种情况下,您将需要明确管理任务生命周期并在完成后将其从队列中删除。所以不要认为这个解决方案对返回任务对象本身有好处。

于 2012-10-25T11:34:19.630 回答