简体   繁体   中英

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. The asio::io_context 's thread for example should still be responsive. 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.).

What is the best (nearly no overhead due to timers for example) way to achieve this with boost::asio? Other coro libraries support something like events.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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