@HamsterGamer

Как работает одновременно асинхронное чтение и запись из примера boost.asio?

Пытаюсь разобрать пример асинхронного чата с офф. сайта:
async_client
async_server
chat_message

Я вроде бы полностью понял клиент, кроме 2-х своих вопросов, которые сильно меня мучают, а именно:

1) io_service::run в отдельном потоке работает как конечный автомат, по принципу очереди, где она коллекционирует асинхронные вызовы. Чтобы io_service.run() не завершался, после подключения, он начинает прослушивать сокет сервера, ожидая сообщение, вот листинг (для подробностей сморите код async_client):
void do_read_header()
    {
        boost::asio::async_read(socket_,
                                boost::asio::buffer(read_msg_.data(), chat_message::header_length),
                                [this](boost::system::error_code ec, std::size_t /*length*/)
                                {
                                    if (!ec && read_msg_.decode_header())
                                    {
                                        do_read_body();
                                    }
                                    else
                                    {
                                        socket_.close();
                                    }
                                });
    }

Однако в потоке функции main, пользователь начинает ввод и добавляет новую задачу в io_service, а именно следующий листинг (для подробностей сморите код async_client) :
void do_write()
    {
        boost::asio::async_write(socket_,
                                 boost::asio::buffer(write_msgs_.front().data(),
                                                     write_msgs_.front().length()),
                                 [this](boost::system::error_code ec, std::size_t /*length*/)
                                 {
                                     if (!ec)
                                     {
                                         write_msgs_.pop_front();
                                         if (!write_msgs_.empty())
                                         {
                                             do_write();
                                         }
                                     }
                                     else
                                     {
                                         socket_.close();
                                     }
                                 });
    }

Но почему async_read не перекрывает async_write? Они же происходят в одном потоке (это я залогировал), почему вытащив async_read из этой очереди и ожидая сообщение, он может одновременно и отправлять сообщения, когда пользователь кладет ему операцию async_write?

2) Потребляют ли операции ресурсы при исполнении, подобные read/async_read/read_some и тп? Или же они работают по принципу condition_variable::wait?
  • Вопрос задан
  • 164 просмотра
Решения вопроса 1
nowm
@nowm
async_read не перекрывает async_write потому, что под капотом происходит не совсем то, что вы видите в исходных кодах. Блоки кода вроде boost::asio::async_read и boost::asio::async_write (я их далее буду называть задачами) во время компиляции разбиваются на более мелкие инструкции. В фоне система может приостанавливать исполнение одной задачи, запоминая все её данные, переключаться на другую задачу и какое-то время исполнять её, потом приостанавливать её исполнение и переключаться на третью задачу, потом снова на первую и так далее по кругу. За счёт этого можно в рамках одного потока выполнять сразу несколько задач. Сама эта система конфигурируется таким образом, чтобы переключаться по очереди этих задач один раз, например, в микросекунду. И таким образом она кусками их выполняет, а у вас складывается впечатление, что они работают параллельно, потому что переключения по очереди происходят очень быстро и зависят не от объёма кода, который нужно выполнить, а от фиксированного времени, которое система готова потратить на работу с одной задачей.

Обычно асинхронность именно так и организуется. Если же исполнение синхронное, то система просто работает над одной задачей и никуда не переключается, пока её не завершит.

Не уверен, что правильно понял ваш второй вопрос. Когда операции выполняются, они естественно потребляют ресурсы. А если их нет в очереди исполнения, то никаких ресурсов не потребляется. Система просто вхолостую раз в микросекунду проверяет очередь задач.

Read время от времени запускается, так что да, определённые ресурсы потребляются, потому что всегда проверяется, если что-нибудь, что нужно прочитать, но там проверяется буффер на предмет данных, а не сам сокет. Так что, ресурсы потребляются, но не настолько сильно, как при чтении существующих данных.

(Раз в микросекунду — это не точное значение, и я его привёл только для примера, чтобы было понятно, что переключение по очереди задач происходит очень часто.)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы