简体   繁体   English

提升 asio,产生一个等待的 function

[英]boost asio, yield an awaitable function

I have several time-consuming computation jobs, which shall not block the executing thread while executing.我有几个耗时的计算作业,它们在执行时不会阻塞正在执行的线程。 I also want to use c++20 coroutines + asio::awaitable 's to accomplish this.我也想使用 c++20 coroutines + asio::awaitable来完成这个。 The asio::io_context 's thread for example should still be responsive.例如asio::io_context的线程应该仍然是响应式的。 Therefore, I want my Job to suspend/yield after some time, and then continue its own execution, when the executioner does not have other jobs to do.因此,当刽子手没有其他工作要做时,我希望我的工作在一段时间后暂停/屈服,然后继续自己的执行。 Also, here are other coroutines with want to await the result of the computations (1:n relation.).此外,这里还有其他想要等待计算结果的协程(1:n 关系)。

What is the best (nearly no overhead due to timers for example) way to achieve this with boost::asio?使用 boost::asio 实现这一目标的最佳方法是什么(例如,由于计时器几乎没有开销)? Other coro libraries support something like events.其他 coro 库支持事件之类的东西。

Here is a code snippet to show my intention:这是一个代码片段来显示我的意图:

MyReturnType result;
CoroEvent coro_event;
auto long_func(asio::ExecutionCtx ctx) -> awaitable<void> {

  for (size_t i{}; i < 1000){
    for (size_t j{}; i < 1000){
      result.compute(i*1000+j);
    }
    ctx.yield();
  }
  coro_event.send();
  co_return;
}

auto get_job_value() -> awaitable<MyReturnType> {
  co_await coro_event.async_wait(use_awaitable); // continues if done, suspends if not.
  co_return result;
}



You can use timers, they are lightweight.您可以使用计时器,它们是轻量级的。

Eg例如

struct CoroEvent {
    using C = asio::steady_timer::clock_type;
    using T = C::time_point;

    void send() { _timer.expires_at(T::min()); }
    auto async_wait(auto&& token) {
        return _timer.async_wait(std::forward<decltype(token)>(token));
    }

    asio::steady_timer _timer{asio::system_executor{}, T::max()};
};

Live On Coliru住在科利鲁

#include <boost/asio.hpp>
#include <coroutine>
#include <iostream>
namespace asio = boost::asio;
using asio::use_awaitable;
using asio::awaitable;

struct MyReturnType {
    void     compute(size_t v) { counter += v; }
    intmax_t counter = 0;
} result;

struct CoroEvent {
    using C = asio::steady_timer::clock_type;
    using T = C::time_point;

    void send() { _timer.expires_at(T::min()); }
    auto async_wait(auto&& token) {
        return _timer.async_wait(std::forward<decltype(token)>(token));
    }

    asio::steady_timer _timer{asio::system_executor{}, T::max()};
} coro_event;

awaitable<void> long_func() {
    for (size_t i = 0; i < 1'000; ++i) {
        for (size_t j = 0; j < 1'000; ++j) {
            result.compute(i * 1'000 + j);
        }

        // co_await std::suspend_always{};
    }
    coro_event.send();
    co_return;
}

awaitable<MyReturnType> get_job_value() {
    co_await coro_event.async_wait(
        use_awaitable); // continues if done, suspends if not.
    co_return result;
}

awaitable<void> consumer(int id, auto output_executor) {
    auto r = co_await get_job_value();

    // synchronize output to prevent a mess
    post(output_executor, [id, sum = r.counter] {
        std::cout << "Received in consumer #" << id << ": " << sum << std::endl;
    });

    co_return;
}

int main() {
    asio::thread_pool ioc;
    asio::co_spawn(ioc, long_func(), asio::detached);

    auto output = make_strand(ioc);
    for (int id = 1; id < 10; ++id)
        asio::co_spawn(ioc, consumer(id, output), asio::detached);

    ioc.join();
}

Prints eg打印例如

Received in consumer #2: 499999500000
Received in consumer #3: 499999500000
Received in consumer #5: 499999500000
Received in consumer #6: 499999500000
Received in consumer #4: 499999500000
Received in consumer #7: 499999500000
Received in consumer #9: 499999500000
Received in consumer #1: 499999500000
Received in consumer #8: 499999500000

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM