将 Boost 链与 std::future 结合使用

Use Boost strand in conjunction with std::future

本文关键字:结合 future 链与 std Boost      更新时间:2023-10-16

我遇到过一个用例,我想将Boost链与std::future结合使用。

为了减少代码重复,我编写了一个通用函数,它将任务发布到提升链并返回未来。

// Some definitions first...
typedef boost::asio::io_service::strand     cb_strand;
typedef std::shared_ptr< cb_strand >        cb_strand_ptr;

代码如下所示:

//////////////////////////////////////////////////////////////////////////
template <class Task>
auto post_future_to_strand(cb_strand_ptr apStrand, Task task)
{
    using return_type = decltype(task());
    auto promise = std::make_shared<std::promise<return_type>>();
    auto future = promise->get_future();
    apStrand->wrap
        (
            [promise, task]()
            {
                try
                {
                    promise->set_value(task());
                }
                catch (...)
                {
                    // LOG ERROR ...

                    // NOTE: Exceptions can be thrown when setting the exception!
                    try
                    {
                        promise->set_exception(std::current_exception());
                    }
                    catch (...)
                    {
                        //LOG ERROR ...
                    }
                }
            }
        );
    return future;
};

然后,我希望将未来发布到一个链上,如以下示例所示:

std::future<int> f = post_future_to_strand(m_apStrand, std::bind(&foo::bar, this))
std::cout << "foo::bar() -> int is " << f.get() << std::endl;

不幸的是,我收到运行时异常:

terminate called after throwing an instance of 'std::future_error'
what():  std::future_error: Broken promise
Signal: SIGABRT (Aborted)

阅读文档后,我想我明白什么是违背承诺以及情况是如何产生的;但是,我觉得我正在 lambda 中捕捉承诺,所以一切都应该很好。 我是这个lambdas世界的新手,所以也许我的理解是错误的。

  • Ubuntu Zesty
  • GCC 6.3(配置为带 cmake 的 C++14(

你包装了任务,但你从不发布它。因此,包装后的任务会立即被破坏,并随之而来的是承诺。

还有另一个陷阱,只有当您在与将来阻塞的线程不同的线程上运行io_service时,事情才有效......否则,您将创建死锁:

  • 生活在科里鲁死锁上

现在您有多个线程,您需要避免服务在首先发布任务之前退出的争用条件。

奖金:

我建议对包装器进行更简单的操作:

template <typename Task>
auto post_future_to_strand(cb_strand_ptr apStrand, Task task)
{
    auto package = std::make_shared<std::packaged_task<decltype(task())()> >(task);
    auto future  = package->get_future();
    apStrand->post([package] { (*package)(); });
    return future;
}

完整演示

住在科里鲁

#include <boost/asio.hpp>
#include <future>
#include <iostream>
using cb_strand_ptr = boost::asio::strand*;
//////////////////////////////////////////////////////////////////////////
template <typename Task>
auto post_future_to_strand(cb_strand_ptr apStrand, Task task)
{
    auto package = std::make_shared<std::packaged_task<decltype(task())()> >(task);
    auto future  = package->get_future();
    apStrand->post([package] { (*package)(); });
    return future;
}
struct Foo {
    boost::asio::strand s;
    cb_strand_ptr m_apStrand = &s;
    Foo(boost::asio::io_service& svc) : s{svc} {}
    void do_it() {
        std::future<int> f = post_future_to_strand(m_apStrand, std::bind(&Foo::bar, this));
        std::cout << "foo::bar() -> int is " << f.get() << std::endl;
    }
    int bar() { 
        return 42; 
    }
};
int main() {
    boost::asio::io_service svc;
    auto lock = std::make_unique<boost::asio::io_service::work>(svc); // prevent premature exit
    std::thread th([&]{ svc.run(); });
    Foo foo(svc);
    foo.do_it(); 
    lock.reset(); // allow service to exit
    th.join();
}

指纹

foo::bar() -> int is 42