Reputation: 591
I want to create an http client using boost asio. To have a structured and optimized I have looked into the examples of boost asio to have some idea of what a good implementation should look like.
Mostly, I have followed the structure of HTTP Server, so I have a connection manager that holds a set of pointers to each individual connection. Now, the big difference here is that already in the constructor of server.cpp an asynchronous function is called, namely
acceptor_.async_accept(new_connection_->socket(), boost::bind(&server::handle_accept, this, boost::asio::placeholders::error));
and in the winmain.cpp the io_service is started through a function call to server::run():
io_service_.run();
In my implementation, since it's a client and not a server, I want to wait for the user to call a send() function before I start connecting to the server. I have therefore moved all connecting-to-server-related function calls into the connection class. When a user requests to send a msg to the server the following is called:
resolver.async_resolve(query, boost::bind(&connection::handle_resolve, boost::ref(*this), boost::asio::placeholders::error, boost::asio::placeholders::iterator)); io_service_.run();
I want to start every connection-object in one separate thread and this is really the background of my question. How do I do that in order to have a structured and optimized code?
I have tried, as HTTP Server 2 example, to set up a thread pool of io_services and assigning work to them so that they will not return until stopped. This seems like a good idea since I would the have the io services running in the background all the time. Consequently, I start the thread pool from my equivalent to server.cpp, in a thread:
boost::thread t(boost::bind(&geocast::enabler::io_service_pool::run, &io_service_pool_));
BUT, from my own trial and error analysis, it seems as you cannot start io_service BEFORE you have issued an asynchronous function, is that true? Because my program gets stuck. In my case I want to call async_resolve only when a user means to sends a POST request or a GET request. To support my theory; The Chat Client starts off by calling an async_connect and having an async_read as callback, this way they can safely call io_service.run() just after the client has been created. I don't want to read from the server all the time just to be able to start the io_service, because that is not how a normal client works, right? A browser does not read from every possible server on the planet without the user having navigated to a website...
If I don't use the thread pool from example 2 but start every connection-class in a separate class, each of which own its own io_service, everything works fine. But, a thread pool with a simple round-robin routine to select an appropriate io_service seems really attractive. What is the best approach for me to go multi-threaded? Am I just picky and should stick to one-connection-one-io_service-thing?
Upvotes: 2
Views: 3374
Reputation: 10206
When using ASIO, you are giving up control of the flow of your program over to ASIO. You can share control if you change your code to use a thread pool and call run_one() instead of run(). run_one() only dispatches one IO job to a thread, so if you have multiple events in the ioservice, you will have to call run_one() several times.
Have you thought about spawning a new thread as your boss thread and then having your boss thread create a bunch of worker threads? Your boss thread could call run() and then your UI's thread could call post() to dispatch a new unit of work. Along with not having to manually call and schedule tasks in the ioservice, it also makes the cleanup and shutdown more straight forward since your boss thread would block when it calls run().
Upvotes: 1
Reputation: 24164
I have tried, as HTTP Server 2 example, to set up a thread pool of io_services and assigning work to them so that they will not return until stopped.
When using asynchronous programming, I strongly suggest using the following designs in order:
io_service
with a single threadio_service
io_service
per thread or other exotic designsYou should only move to the next design if, after profiling, your current design proves to be a bottleneck.
BUT, from my own trial and error analysis, it seems as you cannot start io_service BEFORE you have issued an asynchronous function, is that true?
You are correct, the io_service::run()
documentation spells this out very clearly
The run() function blocks until all work has finished and there are no more handlers to be dispatched, or until the io_service has been stopped.
The correct way to prevent io_service::run()
from returning immediately is to queue up some handlers, or instantiate an io_service::work
object and keep it in scope for as long as you want run()
to stay active.
Upvotes: 1