Reputation: 14869
I have run into a dilemma whilst using boost::asio and boost::io_service
My classes wrap around the async client example provided by boost for socket connections. I use another class which encapsulates:
class service_controller
{
...
/// IO service
boost::asio::io_service __io_service;
/// Endpoint Resolver
boost::asio::ip::tcp::resolver::query __query;
/// Resolution for TCP
boost::asio::ip::tcp::resolver __resolver;
}
So, when I construct my clients, the constructor takes references:
asio_service_client (
boost::asio::ip::tcp::resolver::query & query,
boost::asio::ip::tcp::resolver & resolver,
boost::asio::io_service & io_service
);
Everything works fine, but I have to call
io_service.run()
At the end, after creating all all my clients. If I encapsulate seperate io_service objects for each client, I essentially remove the async io nature, as each one will block until its finished. Therefore, I decided to form a type of group, by making all client objects use the same io_service.
io_service::poll() does not appear to work at all (nothing happens), nor does io_service::run_one().
In fact, the only thing that appears to work, is:
// with a callback => the callback will run once finished
rapp::services::asio_service_client c1( ctrl.Query(), ctrl.Resolver(), ctrl.Services() );
// without a callback => asio_service_client::handle_reply will run once finished
rapp::services::asio_service_client c2 ( ctrl.Query(), ctrl.Resolver(), ctrl.Services() );
rapp::services::asio_service_client c3 ( ctrl.Query(), ctrl.Resolver(), ctrl.Services() );
rapp::services::asio_service_client c4 ( ctrl.Query(), ctrl.Resolver(), ctrl.Services() );
// Run services c1, c2
c1.Run( header, post,
[&]( boost::asio::streambuf & buffer )
{
std::string raw ( ( std::istreambuf_iterator<char>( &buffer ) ), std::istreambuf_iterator<char>() );
std::cout << raw << std::endl;
});
c2.Run( header, post );
ctrl.Services().run();
/// Run remaining services ( c3, c4 )
c3.Run( header, post );
c4.Run( header, post );
ctrl.Services().reset();
ctrl.Services().run();
Unless of course, if I request a group to be run altogether (e.g., ask for c1, c2, c3 and c4 Run).
Is there some way, or some class pattern, where I could automate a queue, where I create objects, add them, and they are run asynchronously? Ideally with threads, but without will also work.
Some kind of a stack, where whilst I add objects, they are asynchronously executed, as they are added.
If I try something like:
Scheduler::Execute ( asio_service_client & client )
{
client.Run( ... )
io_service.reset();
io_service.run();
}
I will reset previous running services, and start all over, which is not what I want. My only obvious option, is to either accept and assign a separate io_service for each added asio_service_client, or force them to be added all together in a job group, which is then executed?
The other solution I can think of, is using threads, thereby, each asio_service_client will run in its own thread, and thus won't block other asio_service_clients, executing in parallel?
Upvotes: 1
Views: 1403
Reputation: 393537
You probably want to share a single io_service
instance and post a io_service::work
object on it so it stays active even if no client currently has any pending asycn operations:
boost::asio::io_service io_service;
auto work = boost::make_shared<boost::asio::io_service::work>(io_service);
// any client can post it's asynchronous operations on this service object, from any thread
// completion handlers will be invoked on any thread that runs `io_service.run()`
// once you want the `io_service` to empty the queue and return:
work.reset();
// now `run()` will return when it runs out of queued tasks
Upvotes: 2