抛出boost :: asio :: spawn没有被try catch捕获

时间:2017-12-23 15:59:44

标签: exception boost coroutine spawn asio

在这个复杂的例子中,两个for循环由boost :: asio :: spawn()异步启动。第一个for循环每1000us打印一个奇数,第二个每1000us打印一个偶数。

我希望输出类似于1 2 3 4 5 6,然后输出错误'应通过调用cerr将消息打印到stderr。

但是,异常实际上是在loop.run()中引发的,所以它不会被try catch块捕获。

有人可以指出在调用spawn()时如何正确捕获runtime_error吗? spawn()不会将生成的协程中抛出的异常重新加到其父作用域吗?

using namespace std;
using namespace boost::asio;
using boost::posix_time::microseconds;

io_service loop;
spawn(loop, [&loop](yield_context yield)
{
    try
    {
        spawn(loop, [&loop](yield_context yield)
        {
            deadline_timer timer{loop};
            for(unsigned i = 0; i < 3; ++i)
            {
                cout << i * 2 + 1 << endl;
                timer.expires_from_now(microseconds(1000));
                timer.async_wait(yield);
            }
            throw runtime_error("Throw an error");
        });

        spawn(loop, [&loop](yield_context yield)
        {
            deadline_timer timer{loop};
            for(unsigned i = 0; i < 3; ++i)
            {
                cout << (i + 1) * 2 << endl;
                timer.expires_from_now(microseconds(1000));
                timer.async_wait(yield);
            }
        });
    } catch(const runtime_error& ex)
    {
        cerr << ex.what() << endl;
    }
});

loop.run();

1 个答案:

答案 0 :(得分:1)

所有处理程序都由服务循环调用,这意味着您始终需要处理错误:Should the exception thrown by boost::asio::io_service::run() be caught?

  

旁注:在协同程序的情况下,我的经验是通过引用捕获有点棘手。这很可能与协程栈本身的生命周期有关。

您可以使用以下方式演示:

while (true) {
    try {
        loop.run();
        break;
    } catch(std::runtime_error ex) {
        std::cerr << "L:" << __LINE__ << ": " << ex.what() << "\n";
    }
}

其他注释

请注意,传递error_code错误可以通过两种方式在惯用的Asio界面中完成,包括Coroutines:How to set error_code to asio::yield_context

完整样本

简单地使您的样品适应自我:

<强> Live On Coliru

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/high_resolution_timer.hpp>

using namespace std::chrono_literals;

int main() {
    boost::asio::io_service loop;

    spawn(loop, [&loop](boost::asio::yield_context yield)
    {
        try
        {
            spawn(yield, [&loop](boost::asio::yield_context yield)
            {
                boost::asio::high_resolution_timer timer{loop};
                for(unsigned i = 0; i < 3; ++i)
                {
                    std::cout << i * 2 + 1 << std::endl;
                    timer.expires_from_now(100ms);
                    timer.async_wait(yield);
                }
                throw std::system_error(ENOENT, std::system_category(), "Throw an error");
                //throw boost::system::system_error(ENOENT, boost::system::system_category(), "Throw an error");
            });

            spawn(yield, [&loop](boost::asio::yield_context yield)
            {
                boost::asio::high_resolution_timer timer{loop};
                for(unsigned i = 0; i < 3; ++i)
                {
                    std::cout << (i + 1) * 2 << std::endl;
                    timer.expires_from_now(100ms);
                    timer.async_wait(yield);
                }
            });
        } catch(const std::runtime_error& ex)
        {
            std::cerr << "L:" << __LINE__ << ": " << ex.what() << "\n";
        }
    });

    while (true) {
        try {
            loop.run();
            break;
        } catch(std::runtime_error ex) {
            std::cerr << "L:" << __LINE__ << ": " << ex.what() << "\n";
        }
    }
}

打印

1
2
3
4
5
6
L:49: Throw an error: No such file or directory

奖金:

使用广义竞争令牌async_result进行额外的处理:

<强> Live On Coliru

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/high_resolution_timer.hpp>

using namespace std::chrono_literals;

boost::asio::io_service loop;

template <typename Token>
auto async_foo(bool success, Token&& token)
{
    typename boost::asio::handler_type<Token, void(boost::system::error_code, int)>::type
                 handler (std::forward<Token> (token));

    boost::asio::async_result<decltype (handler)> result (handler);

    boost::asio::yield_context yield(token);

    boost::asio::high_resolution_timer timer{loop};
    for(unsigned i = 0; i < 3; ++i) {
        std::cout << (i * 2 + (success?0:1)) << std::endl;
        timer.expires_from_now(100ms);
        timer.async_wait(yield);
    }

    if (success)
        handler(42);
    else
        throw boost::system::system_error(ENOENT, boost::system::system_category(), "Throw an error");

    return result.get();
}

int main() {

    auto spawn_foo = [](bool success) {
        spawn(loop, [=](boost::asio::yield_context yield) {
            try
            {
                int answer = async_foo(success, yield);
                std::cout << "async_foo returned " << answer << std::endl;
            } catch(const std::runtime_error& ex)
            {
                std::cerr << "L:" << __LINE__ << ": " << ex.what() << std::endl;
            }
        });
    };

    spawn_foo(true);
    spawn_foo(false);

    loop.run();
}

打印

0
1
2
3
4
5
async_foo returned 42
L:45: Throw an error: No such file or directory