前言
基本上這個要求蠻奇怪的,ASIO又不是沒提供Synchronize API,不過有些事情就是只有Asynchronous API能做到
例如我要在五秒鐘之內連線,五秒鐘之內無法連上就直接結束,如果用Synchronize API,Timeout由作業系統決定
這個時候就只有自己寫了
use_future
ASIO有一個feature,可以將Async operation轉成Sync operation
一般來說我們的程式碼會寫成這樣
1 2 3
| socket.async_connect(endpoint, [](std::error_code ec) { });
|
但是如果我們用use_future
的話,ASIO內部會自己轉成promise/future的Pattern
這適合在Threead synchronize的情景使用
1 2 3 4 5 6 7
| asio::io_context ctx; asio::ip::tcp::socket socket(ctx); auto future = socket.async_connect(endpoint, asio::use_future); std::thread t([&] { ctx.run(); }); future.get();
|
Combie with C++20 Coroutine
如果我們的條件更複雜,如一開始寫的五秒鐘Timeout這件事,上面的程式碼就不敷使用,
如果用原先的Function callback方式寫大概會死一堆腦細胞,而Coroutine可以讓我們大大減輕心智負擔
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| asio::awaitable<void> timeout(std::chrono::seconds seconds) { asio::steady_timer timer(co_await asio::this_coro::executor); timer.expires_after(seconds); co_await timer.async_wait(use_nothrow_awaitable); }
asio::awaitable<std::error_code> connect_with_timeout( asio::ip::tcp::socket& socket, const asio::ip::tcp::endpoint& endpoint) { using namespace asio::experimental::awaitable_operators; auto result = co_await( socket.async_connect(endpoint, use_nothrow_awaitable) || timeout(std::chrono::seconds(5)) ); if (result.index() == 1) { co_return asio::error::timed_out; } auto [r] = std::get<0>(result); co_return r; }
asio::io_context io_context; auto connect_future = asio::co_spawn( io_context.get_executor(), connect_with_timeout(asio::ip::tcp::socket(io_context), endpoint), asio::use_future); io_context.run(); return connect_future.get();
|
如上面程式碼寫的一樣
在connect_with_timeout
有兩種可能,一個是socket connect的結果,另外一個是timeout
asio::co_spawn的最後一個參數不是教學中的detach
,而是剛剛講的use_future
這樣子就可以把Coroutine 和 promise/future一起使用