boost::asio::spawn 中的异常抛出未被 try catch 捕获

Exception throw in boost::asio::spawn not caught by try catch

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

我希望输出类似于 1 2 3 4 5 6,然后应该通过调用 cerr 将 'Throw an error' 消息打印到 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();

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

Sidenote: In the case of coroutines, it's my experience that catching by reference is a bit tricky. This more than likely has to do with the lifetime of the coroutine stack itself.

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

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

其他注意事项

请注意,传递 error_code 错误可以通过惯用的 Asio 接口以两种方式完成,包括协程: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