如何指定带有超时的`boost::asio::yield_context`?

问题描述 投票:0回答:2

我想学习如何将超时计时器传递给

boost::asio::yield_context

比方说,就

Boost 1.80
而言,有类似以下内容:

#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>

void async_func_0(boost::asio::yield_context yield) {
  async_func_1(yield);
}

void async_func_1(boost::asio::yield_context) {
}

int main() {
  boost::asio::io_context ioc;
  boost::asio::spawn(ioc.get_executor(), &async_func_0);
  ioc.run();
  return 0;  
}

让我们想象一下

async_func_1
是一个相当大的负担,它是通过
async
实现的
boost::coroutines
(因为
boost::asio
由于某些未知原因不使用
boost::coroutines2
)并且它可以工作不可预测的长时间,主要是在
 io
操作。

一个好主意是用

async_func_1
指定对
timeout
的调用,这样如果时间过去了,它必须返回任何有错误的内容。假设在
boost::asio::yield_context
内最近使用
async_func_1

但是我很困惑应该如何用

boost::asio
来表达。

附注举个例子,在

Rust
中,它会像下面这样:

use std::time::Duration;
use futures_time::FutureExt;

async fn func_0() {
  func_1().timeout(Duration::from_secs(60)).await;
}

async fn func_1() {
}

#[tokio::main]
async fn main() {
  tokio::task::spawn(func_0());
}
c++ boost boost-asio boost-coroutine boost-timer
2个回答
1
投票

在 Asio 中,取消和执行者是不同的问题。

这很灵活。这也意味着您必须编写自己的超时代码。

一个非常粗略的想法:

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <iostream>
namespace asio = boost::asio;
using boost::asio::yield_context;
using namespace std::chrono_literals;
using boost::system::error_code;

static std::chrono::steady_clock::duration s_timeout = 500ms;


template <typename Token>
void async_func_1(Token token) {
    error_code ec;

    // emulating a long IO bound task
    asio::steady_timer work(get_associated_executor(token), 1s);
    work.async_wait(redirect_error(token, ec));

    std::cout << "async_func_1 completion: " << ec.message() << std::endl;
}

void async_func_0(yield_context yield) {
    asio::cancellation_signal cancel;

    auto cyield = asio::bind_cancellation_slot(cancel.slot(), yield);

    std::cout << "async_func_0 deadline at " << s_timeout / 1.0s << "s" << std::endl;

    asio::steady_timer deadline(get_associated_executor(cyield), s_timeout);
    deadline.async_wait([&](error_code ec) {
        std::cout << "Timeout: " << ec.message() << std::endl;
        if (!ec)
            cancel.emit(asio::cancellation_type::terminal);
    });

    async_func_1(cyield);

    std::cout << "async_func_0 completion" << std::endl;
}

int main(int argc, char** argv) {
    if (argc>1)
        s_timeout = 1ms * atoi(argv[1]);

    boost::asio::io_context ioc;
    spawn(ioc.get_executor(), async_func_0);

    ioc.run();
}

目前没有接受此的在线编译器能够运行此。这是本地输出:

for t in 150 1500; do time ./build/sotest "$t" 2>"$t.trace"; ~/custom/superboost/libs/asio/tools/handlerviz.pl < "$t.trace" | dot -T png -o trace_$t.png; done

async_func_0 deadline at 0.15s
Timeout: Success
async_func_1 completion: Operation canceled
async_func_0 completion

real    0m0,170s
user    0m0,009s
sys     0m0,011s
async_func_0 deadline at 1.5s
async_func_1 completion: Success
async_func_0 completion
Timeout: Operation canceled

real    0m1,021s
user    0m0,011s
sys     0m0,011s

处理程序可视化:

1 魔杖盒coliruCE


路从这里开始

你可能会说这很麻烦。与 Rust 库功能相比,它确实如此。要在 Asio 中库这个,你可以

  • 从类型
    yield_context
    派生您自己的完成令牌,添加您想要的行为
  • 进行组合操作(例如 使用
    deferred

0
投票

我想补充 @sehe 的答案,至少在

async_func_0()
中,可能存在带有
success
错误代码的虚假回调调用,而函数中的所有对象都已被销毁,这将导致UB 或崩溃。 请参阅 ASIO 计时器 `cancel()` 可以调用虚假的“成功”吗? 了解更多详细信息。

解决该问题的方法是添加完成布尔标志:

void async_func_0(yield_context yield) {
    asio::cancellation_signal cancel;
    auto isCompleted = std::make_shared<bool>(false);

    auto cyield = asio::bind_cancellation_slot(cancel.slot(), yield);

    std::cout << "async_func_0 deadline at " << s_timeout / 1.0s << "s" << std::endl;

    asio::steady_timer deadline(get_associated_executor(cyield), s_timeout);
    deadline.async_wait([&cancel, isCompleted](error_code ec) {
        std::cout << "Timeout: " << ec.message() << std::endl;
        if (!ec && !(*isCompleted))
            cancel.emit(asio::cancellation_type::terminal);
    });

    async_func_1(cyield);
    *isCompleted = true;

    std::cout << "async_func_0 completion" << std::endl;
}
© www.soinside.com 2019 - 2024. All rights reserved.