Задать вопрос
@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?
  • Вопрос задан
  • 508 просмотров
Подписаться 2 Средний Комментировать
Решения вопроса 1
nowm
@nowm
async_read не перекрывает async_write потому, что под капотом происходит не совсем то, что вы видите в исходных кодах. Блоки кода вроде boost::asio::async_read и boost::asio::async_write (я их далее буду называть задачами) во время компиляции разбиваются на более мелкие инструкции. В фоне система может приостанавливать исполнение одной задачи, запоминая все её данные, переключаться на другую задачу и какое-то время исполнять её, потом приостанавливать её исполнение и переключаться на третью задачу, потом снова на первую и так далее по кругу. За счёт этого можно в рамках одного потока выполнять сразу несколько задач. Сама эта система конфигурируется таким образом, чтобы переключаться по очереди этих задач один раз, например, в микросекунду. И таким образом она кусками их выполняет, а у вас складывается впечатление, что они работают параллельно, потому что переключения по очереди происходят очень быстро и зависят не от объёма кода, который нужно выполнить, а от фиксированного времени, которое система готова потратить на работу с одной задачей.

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

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

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

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

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

Похожие вопросы