実行時間がかかる処理 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
ただし, スケジューリングのタイミングがシビアなので, 実行時間がかかるものは別スレッドで実行した方がいいでしょう.