あめだまふぁくとりー

Boost.Graphとかできますん

Asio の coroutine で明示的に yield する

実行時間がかかる処理 A を coroutine の中で行うと, その coroutine と同じ strand 内の処理は処理 A が完了するまで待たされてしまいます.

io_service::post または strand::post を使用することで, 処理 A の合間で明示的に coroutine を切り替えることができます (dispatch は使用できません. 必ず post を使用してください).

#include <ctime>
#include <chrono>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>

using namespace boost;

void heavy_task()
{
    std::this_thread::sleep_for(std::chrono::seconds{1});
}

auto print_now(char const* str)
    -> std::ostream&
{
    auto const now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    return std::cout << str << " " << std::ctime(&now);
}

int main()
{
    asio::io_service io_service{};

    auto strand = asio::io_service::strand{io_service};

    // 実行時間のかかる処理 A
    asio::spawn(strand, [&](asio::yield_context yield) {
        print_now("start heavy_task");
        for (auto i = 0; i < 5; ++i) {
            io_service.post(yield); // ★ ここで切り替え
            heavy_task();
            io_service.post(yield); // ★ ここでも切り替え
        }
        print_now("finish heavy_task");
    });

    // 処理 A と同じ strand 内の処理
    asio::spawn(strand, [&](asio::yield_context yield) {
        asio::steady_timer timer{io_service};
        for (auto i = 0; i < 4; ++i) {
            timer.expires_from_now(std::chrono::seconds{1});
            timer.async_wait(yield);
            print_now("tick tack...    ");
        }
    });

    io_service.run();
}

明示的に切り替えた場合の実行結果

start heavy_task Tue Dec 23 11:21:13 2014
tick tack...     Tue Dec 23 11:21:14 2014
tick tack...     Tue Dec 23 11:21:15 2014
tick tack...     Tue Dec 23 11:21:16 2014
tick tack...     Tue Dec 23 11:21:17 2014
finish heavy_task Tue Dec 23 11:21:18 2014

切り替えない場合の実行結果

start heavy_task Tue Dec 23 11:32:17 2014
finish heavy_task Tue Dec 23 11:32:22 2014
tick tack...     Tue Dec 23 11:32:23 2014
tick tack...     Tue Dec 23 11:32:24 2014
tick tack...     Tue Dec 23 11:32:25 2014
tick tack...     Tue Dec 23 11:32:26 2014

ただし, スケジューリングのタイミングがシビアなので, 実行時間がかかるものは別スレッドで実行した方がいいでしょう.