I have a simple asynchronous server, heavily inspired from the HTTP server example in boost asio documentation (single-threaded server), which processes requests sent by clients.
My server
class creates a new connection
object everytime a new client connects and calls its start()
method (as in the HTTP server example). A connection
instance reads the client's request and sends a reply afterwards using asynchronous operations (ie boost::asio::async_read
and boost::asio::async_write
)
Here is a simplified version of the connection
class :
void connection::start() {
// Read request from a client
boost::asio::mutable_buffers_1 read_buffer = boost::asio::buffer(
buffer_data_, REQUET_SIZE);
boost::asio::async_read(socket_, read_buffer,
boost::bind(&connection::handle_read_request, shared_from_this(),
read_buffer, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
// Error handling omitted for the sake of brievty
void connection::handle_read_request(boost::asio::mutable_buffers_1& buffer,
const boost::system::error_code& e, std::size_t bytes_transferred) {
request req = parse_request(buffer);
if(req.type_ = REQUEST_TYPE_1) {
reply rep(...........);
rep.prepare_buffer(buffer_data_.c_array());
// Send the request using async_write
boost::asio::async_write(socket_,
boost::asio::buffer(buffer_data_, rep.required_buffer_size()),
boost::bind(&connection::stop, shared_from_this()));
} else if(req.type_ = REQUEST_TYPE_2 {
// Need to do heavy computational task
}
}
All of this works very well, however, in some cases, I need to perform heavy computational tasks ( REQUEST_TYPE_2
). I can't perform these tasks in my handle_read_request
because they would block the single-threaded server and prevent other clients from begin served.
Ideally, I would like to submit my heavy computational task to a thread pool and run a method (eg connection::handle_done_task(std::string computation_result)
) of my connection class on completion of the task. This handle_done_task(std::string computation_result)
would send the result of the computation to the client (using boost::asio::async_write
).
How can do I that ? Are there some issues I should be aware of (Is it safe to call boost::asio::async_write
on the same socket from mutiple threads) ?
As the documentation states explicitly, asio objects (except for strand
/ io_service
) are not thread-safe, so you shouldn't call async_write
from multiple threads without synchronization. Instead, use post-to-io_service idiom. Like this:
// pseudocode, untested!
if (req.type_ = REQUEST_TYPE_2)
{
auto self = shared_from_this(); // lets capture shared_ptr<connection> to ensure its lifespan
auto handler = [=](computation_result_type res)
{
// post the function that accesses asio i/o objects to `io_service` thread
self->io_->post([] { handle_done_task(res); });
}
thread worker([=](const std::function<(computation_result_type res)> &handler)
{
// do the heavy work...
// then invoke the handler
handler(result);
});
worker.detach();
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.