I am currently creating multiple task_sub()
at the beginning of the task_main()
function, and waiting for these task_sub()
to end before exiting task_main()
. There is only one thread. What should I do?
Is there any way to achieve this requirement? Note: task_main()
and task_sub()
are not at the same level, and task_sub()
must be created in task_main()
.
asio 1.31.0
awaitable<void> task_sub()
{
// TODO something
}
awaitable<void> task_main()
{
printf("task_main begin\n");
// create some task_sub and execute
auto task1 = co_spawn(co_await this_coro::executor, task_sub(), xxxxxx?);
auto task2 = co_spawn(co_await this_coro::executor, task_sub(), xxxxxx?);
// main loop
// TODO something
// waiting task
co_await task1;
co_await task2;
printf("task_main end\n");
}
I have tried using use_future
to wait for the task to end, but it does not support co_await
and task1.wait()
is sync and blocked, which will block the main loop.
awaitable<void> task_main()
{
printf("task_main begin\n");
// create some task_sub and execute
//
std::future<void> task1 = co_spawn(co_await this_coro::executor, task_sub(), use_future);
std::future<void> task2 = co_spawn(co_await this_coro::executor, task_sub(), use_future);
// main loop
// TODO something
// waiting task
//co_await task1;
//co_await task2;
//is blocked
task1.wait();
task2.wait();
printf("task_main end\n");
}
I have also tried using channel
, it can work, but it's not beautiful
//...
#include <asio/experimental/channel.hpp>
using exit_channel = asio::experimental::channel<void(asio::error_code)>;
awaitable<void> task_sub(exit_channel& channel)
{
// TODO something
//end
co_await channel.async_send(error_code{});
}
awaitable<void> task_main()
{
printf("task_main begin\n");
auto ctx = co_await this_coro::executor;
// create some task_sub and execute
exit_channel task1_channel(ctx, 1);
exit_channel task2_channel(ctx, 1);
co_spawn(ctx, task_sub(task1_channel), detached);
co_spawn(ctx, task_sub(task2_channel), detached);
// main loop
// TODO something
// waiting task
co_await task1_channel.async_receive();
co_await task2_channel.async_receive();
printf("task_main end\n");
}
And co_spawn(,,use_awaitable)
, it won't start executing at co_spawn
, so it's not what you want.
Like I answered previously on the question you deleted, you can use use_promise
:
#include <asio.hpp>
#include <asio/experimental/promise.hpp>
#include <asio/experimental/use_promise.hpp>
#include <stdio.h>
using asio::awaitable;
using asio::experimental::use_promise;
using namespace std::chrono_literals;
namespace this_coro = asio::this_coro;
awaitable<void> delay(auto dur) {
co_await asio::steady_timer{co_await this_coro::executor, dur}.async_wait();
}
awaitable<void> task_sub(char const* caption, int n, auto ival) {
for (int i = 0; i < n; ++i) {
printf("task_sub '%s' %d\n", caption, i);
co_await delay(ival);
}
}
awaitable<void> task_main()
{
printf("task_main begin\n");
auto ex = co_await this_coro::executor;
// create some task_sub and execute
auto task1 = co_spawn(ex, task_sub("task1", 5, 500ms), use_promise);
auto task2 = co_spawn(ex, task_sub("task2", 4, 750ms), use_promise);
// main loop
// TODO something
co_await task_sub("task_main", 3, 1000ms);
printf("task_main wait\n");
co_await std::move(task1);
co_await std::move(task2);
printf("task_main end\n");
}
int main() {
asio::io_context ctx;
co_spawn(ctx, task_main(), asio::detached);
ctx.run();
}
See it live:
¹ using Boost Asio online, because it doesn't have standalone Asio
I'd still suggest parallel groups with awaitable operators for syntactic sugar:
#include <boost/asio.hpp>
#include <boost/asio/experimental/promise.hpp>
#include <boost/asio/experimental/use_promise.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <stdio.h>
namespace asio = boost::asio;
using asio::awaitable;
using asio::experimental::use_promise;
using namespace std::chrono_literals;
using namespace asio::experimental::awaitable_operators;
namespace this_coro = asio::this_coro;
awaitable<void> delay(auto dur) {
co_await asio::steady_timer{co_await this_coro::executor, dur}.async_wait();
}
awaitable<void> task_sub(char const* caption, int n, auto ival) {
for (int i = 0; i < n; ++i) {
printf("task_sub '%s' %d\n", caption, i);
co_await delay(ival);
}
printf("task_sub '%s' done\n", caption);
}
awaitable<void> task_main() {
printf("task_main begin\n");
auto ex = co_await this_coro::executor;
co_await ( //
task_sub("task1", 5, 500ms) //
&& task_sub("task2", 4, 750ms) //
&& task_sub("task_main", 3, 1000ms) //
);
printf("task_main end\n");
}
int main() {
asio::io_context ctx;
co_spawn(ctx, task_main(), asio::detached);
ctx.run();
}
Printing
task_main begin
task_sub 'task1' 0
task_sub 'task2' 0
task_sub 'task_main' 0
task_sub 'task1' 1
task_sub 'task2' 1
task_sub 'task_main' 1
task_sub 'task1' 2
task_sub 'task1' 3
task_sub 'task2' 2
task_sub 'task_main' 2
task_sub 'task1' 4
task_sub 'task2' 3
task_sub 'task1' done
task_sub 'task_main' done
task_sub 'task2' done
task_main end