使用Boost strand和std::future结合使用

3

我遇到了一个使用案例,希望在std::future和Boost strand结合使用。

为了减少代码重复,我编写了一个通用函数,可以将任务发布到Boost strand并返回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;
};

我希望您能够将以下示例中展示的内容发布到一个串中,该内容与IT技术相关。请润色并使其更加易于理解,但不要删除HTML标签。请按照格式要求仅返回翻译结果。
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)

我看完文档后,我认为我理解了什么是"broken promise"以及产生这种情况的原因;然而,我觉得我已经在lambda中捕捉了这个promise,所以一切都应该没问题。由于我是一个初学者,所以可能我的理解是错误的。

  • Ubuntu Zesty
  • GCC 6.3(使用cmake配置为C++14)
1个回答

4
你将任务进行了包装,但却未发布它。因此,被包装的任务立即被销毁,并随之而来的是Promise。
还有一个陷阱,只有在你在不同于阻塞future的线程上运行io_service时,事情才能正常运作...否则就会出现死锁: 现在你有多个线程,需要避免在任务被发布之前服务退出的竞争条件。

Bonus:

I'd suggest a far simpler take on the wrapper:

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;
}

Full Demo

Live On Coliru

#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

谢谢你的解答 - 这个解决方案简单、精炼而优雅。有点不好意思,我竟然没有调用 post(..) :/我的后续问题之一与死锁有关。在 Stack Overflow 上有一个类似的问题,但我不明白在什么条件下会出现死锁。通过你的解释,现在问题完全清楚了。再次感谢。 - ZeroDefect

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接