Я смотрю на пример Boost Asio Blocking TCP Client timeout с особым интересом к тому, как реализованы тайм-ауты соединения. Как мы узнаем из документации, что обработчик обратного вызова и последующие проверки не вызывают состояния гонки?
Команда асинхронного соединения
boost::asio::async_connect(socket_, iter, var(ec) = _1);
выполняет var(ec) = _1
, который является обработчиком для установки кода ошибки после выполнения. В качестве альтернативы здесь можно использовать полную и явную лямбду.
В то же время функция check_deadline
, похоже, вызывается членом deadline_
. Тайм-аут, по-видимому, обеспечивается принудительным закрытием сокета в течение крайнего срока, после чего мы предполагаем, что, возможно, вернется блокирующий оператор
do io_service_.run_one(); while (ec == boost::asio::error::would_block);
. Сначала я подумал, что код ошибки должен быть atomi c, но похоже, что это не так. Вместо этого эта страница , по-видимому, указывает на то, что модель цепочки будет работать всякий раз, когда вызовы сокета / контекста поступают из одного и того же потока.
Таким образом, мы предполагаем, что каждый обратный вызов для крайнего срока ( который находится в Asio), а дескриптор подпрограммы async_connect
не будет выполняться одновременно. Такие страницы, как this в документации, намекают, что обработчики будут выполняться только во время run()
вызовов, что предотвратит выполнение команды while(ec == whatever)
сзади, когда обработчик в настоящее время изменяет свое значение.
Как я знаю это явно? Что в документации прямо говорит мне, что никакие обработчики никогда не будут выполняться вне этих подпрограмм? Если это правда, страница в шаблоне проектирования проактора должна предполагать это, но никогда явно, где «Инициатор» ведет к «Обработчику завершения».
Я обнаружил закрытие документация для io_context говоря
Синхронные операции с объектами ввода-вывода неявно запускают объект io_context для отдельной операции. Функции io_context run (), run_one (), run_for (), run_until (), poll () или poll_one () должны вызываться для того, чтобы io_context выполнял асинхронные операции от имени программы C ++. Уведомление о завершении асинхронной операции доставляется путем вызова связанного обработчика. Обработчики вызываются только потоком, который в настоящее время вызывает любую перегрузку run (), run_one (), run_for (), run_until (), poll () или poll_one () для io_context.
Это означает, что если у меня есть один поток, выполняющий команду run_one()
, то его путь управления будет ждать, пока не станет доступен обработчик, и, в конечном итоге, пройдет через обработчик, после чего он вернется и проверит значение ec
.
Правильно ли это, и это «Обработчики вызываются только потоком, который в настоящее время вызывает любую перегрузку run (), run_one (), run_for (), run_until (), poll () или poll_one () для io_context». Лучшее утверждение для понимания того, как код всегда будет работать? Есть ли другая экспозиция?