Boost asio concurrency

I am studying the use of Boost ASIO. Below is the code copied from the chat example along with the Boost ASIO documentation,

typedef std::deque<chat_message> chat_message_queue; class chat_client { public: chat_client(boost::asio::io_service& io_service, tcp::resolver::iterator endpoint_iterator) : io_service_(io_service), socket_(io_service) { boost::asio::async_connect(socket_, endpoint_iterator, boost::bind(&chat_client::handle_connect, this, boost::asio::placeholders::error)); } void write(const chat_message& msg) { io_service_.post(boost::bind(&chat_client::do_write, this, msg)); } void close() { io_service_.post(boost::bind(&chat_client::do_close, this)); } private: void handle_connect(const boost::system::error_code& error) { //Implementation } void handle_read_header(const boost::system::error_code& error) { //Implementation } void handle_read_body(const boost::system::error_code& error) { //Implementation } void do_write(chat_message msg) { bool write_in_progress = !write_msgs_.empty(); write_msgs_.push_back(msg); if (!write_in_progress) { boost::asio::async_write(socket_, boost::asio::buffer(write_msgs_.front().data(), write_msgs_.front().length()), boost::bind(&chat_client::handle_write, this, boost::asio::placeholders::error)); } } void handle_write(const boost::system::error_code& error) { //Implementation } void do_close() { socket_.close(); } private: boost::asio::io_service& io_service_; tcp::socket socket_; chat_message read_msg_; chat_message_queue write_msgs_; }; 
  • The write_msgs_ is asynchronous and there are no locks around the member variables write_msgs_ and read_msgs_ . Should there be a problem with concurrency here?

  • Is it safe to call post calls from a thread that runs io_service::run? What about dispatch ? What to do with the same call from threads that do not work io_service::run ?

  • In doSend() , why do they push the message on write_msgs_ instead of sending directly? Also in the same function, why do they check if write_msgs_ empty, and only if it is not, by sending? Does write_msgs_.empty() = false record? How?

  • If do_write() is called in only one thread, why do I need a queue to maintain the send sequence? Wouldn't io_service finish the task and then do the asynchronous operation do_write by do_write ? Will using dispatch instead of post matter in the example mentioned above?

+4
source share
1 answer
  • Although entries are asynchronous, there is no multithreading here: do_write() is called on a single thread. Of course, the buffer to be sent must be alive and immutable until a termination handler is called.

  • It is safe to call post () and dispatch () from any thread. Read the thread safety section of the io_service documentation .

  • If async_write is async_write and you call async_write on the same socket, the order in which the data will be sent will be undefined. In other words, the data will be corrupted. The easiest way to get around this is to make a message queue: each time async_write completes, it displays another async_write . (By the way, the same applies to async_read .) Why write_msgs_.empty() = false means that the recording continues? Since write_msgs_ not empty, handle_write (the completion handler of the previous async_write ) produces another async_write . This loop breaks when write_msgs_ empty.

  • Read what the documentation says about async_write :

    This operation is performed in terms of zero or more calls to stream async_write_some and is known as an operation . The program must ensure that the stream does not perform any other write operations (for example, async_write, the stream of the async_write_some function, or any other composed operations that write) until this operation completes.

As for dispatch vs post - as far as I can see, in the above example they are interchangeable. Using a message is important if we do not want the calling functor to exit synchronously.

+8
source

All Articles