My problem is the following. I start several operations asynchronously, and I want to continue until all of them are finished. Using Boost Asio, the most straightforward way to do this is the following. Suppose tasks
is some kind of container of objects that support some asynchronous operation.
tasksToGo = tasks.size();
for (auto task: tasks) {
task.async_do_something([](const boost::system::error_code& ec)
{
if (ec) {
// handle error
} else {
if (--taslsToGo == 0) {
tasksFinished();
}
}
});
}
The problem with this solution is that it feels like a workaround. In Boost 1.54 I can do it with futures but I can only wait synchronously, which is only possible from a thread separate from where run()
is called.
for (auto task: tasks) {
futures.push_back(task.async_do_something(boost::asio::use_future));
}
for (auto future: futures) {
future.wait();
}
This code is much clearer than the previous one, but I need a separate thread which I don't want. I want something that can be used like this:
for (auto task: tasks) {
futures.push_back(task.async_do_something(boost::asio::use_future));
}
boost::asio::spawn(ioService, [](boost::asio::yield_context yield)
{
for (auto future: futures) {
future.async_wait(yield);
}
tasksFinished();
}
Is there anything that can be used similarly?
As far as I know, there is currently no first-class support for this. However, given the direction of the library, I would be surprised if this functionality was not available in the future.
A few papers have been proposed to add support for this type of functionality:
N3558 - A Standardized Representation of Asynchronous Operations is particularly interesting. It proposes
when_all(futures)
andfuture.next()
. If it is implemented, then it would be possible to represent the asynchronous chain as:async
can execute. For Boost.Asio, this would likely require providing some type of executor that defers to theio_service
.While these papers are still ongoing, it may be worthwhile to periodically check Boost.Thread's Conformance and Extension page and Boost.Asio's github for early adaptations of these proposals.
I had the need for this functionality a year ago with a much earlier version of Boost so worked on my own solution. There are still some rough areas with regards to the semantics, but it may be helpful as a reference material until something official is adopted. Before I provide the code, here is an example application based on your question:
Which produces the following output:
And here is
async_ops.hpp
:Here are some basic to advance examples using the above code with two threads. My notation:
a -> b
expressesa
thenb
(a | b)
expressesa
orb
. Thus(a | b) -> c
implies when eithera
orb
finish, then runc
.(a & b)
expressesa
andb
. Thus(a & b) -> c
implies when botha
andb
finish, then runc
.Before each case, I print the chain's notation. Additionally, each function will print a capital letter when entering, and a lower letter when exiting.
Produces the following output:
How about using the work concept? io_service::run will run as long as the work is available and will terminate when work is deleted as soon as there is no unfinished task.
Before calling run you create a work instance:
And in your other thread you call as soon as you want to allow io_servce::run to terminate.
see also http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work