Необходимы некоторые пояснения относительно синхронных и асинхронных операций asio - PullRequest
19 голосов
/ 12 марта 2011

Насколько я знаю, основное отличие между синхронными и асинхронными операциями.Т.е. write() или read() против async_write() и async_read() означает, что первые не возвращаются до завершения операции или ошибок, а последние возвращаются немедленно.

Из-затот факт, что асинхронные операции управляются io_service.run(), который не завершается, пока контролируемые операции не будут завершены.Мне кажется, что в последовательных операциях, как те, которые участвуют в соединениях TCP / IP с протоколами, такими как POP3, в которых операция представляет собой последовательность, такую ​​как:

 C: <connect>
 S: Ok.
 C: User...
 S: Ok.
 C: Password
 S: Ok.
 C: Command
 S: answer
 C: Command
 S: answer
 ...
 C: bye
 S: <close>

Разница между синхронными / асинхронными операторами неимеет большой смысл.

Конечно, в обеих операциях всегда существует риск того, что поток программы останавливается на неопределенный срок из-за некоторых обстоятельств - например, из-за использования таймеров - но я хотел бы знать некоторые более авторитетные мнения по этому вопросу..

Я должен признать, что вопрос довольно плохо определен, но я хотел бы услышать несколько советов о том, когда использовать один или другой.Я сталкивался с проблемами при отладке в MS Visual Studio, касающихся асинхронных операций SSL в клиенте POP3, над которым я сейчас работаю, и иногда думаю, что, возможно, это плохая идея - использовать асинхронный в этом случае.

Ответы [ 4 ]

33 голосов
/ 13 марта 2011

Документация Boost.Asio действительно проделывает фантастическую работу, объясняя две концепции.Как упомянул Ральф, Крис также имеет отличный блог, описывающий асинхронные концепции.Пример парковочного счетчика , объясняющий, как работают тайм-ауты, особенно интересен, как пример bind, показанный .

Сначала рассмотрим операцию синхронного соединения:

synchronous connect

Поток управления здесь довольно прост, ваша программа вызывает некоторый API (1) для подключения сокета.API использует сервис ввода-вывода (2) для выполнения операции в операционной системе (3).После завершения этой операции (4 и 5) управление сразу же возвращается к вашей программе (6) с некоторым указанием на успех или неудачу.

Аналогичная асинхронная операция имеет совершенно другой поток управления:

asynchronous connect

Здесь ваше приложение инициирует операцию (1), используя ту же службу ввода-вывода (2), но поток управления инвертируется.Завершение операции приводит к тому, что служба ввода-вывода уведомляет вашу программу через обработчик завершения.Время между шагом 3 и завершением операции полностью содержалось в операции соединения для синхронного случая.

Вы можете видеть, что синхронный случай, естественно, легче понять большинству программистов, поскольку он представляет традиционный поток управленияпарадигм.Инверсный поток управления, используемый асинхронными операциями, трудно понять, он часто вынуждает вашу программу разделять операции на методы start и handle, в которых логика смещается.Однако, как только вы поймете основы этого потока управления, вы поймете, насколько мощна эта концепция.Некоторые из преимуществ асинхронного программирования:

  • Отсоединяет потоки от параллелизма.Возьмите длительную операцию, для синхронного случая вы часто создаете отдельный поток для обработки этой операции, чтобы не дать GUI приложения перестать отвечать на запросы.Эта концепция прекрасно работает в небольшом масштабе, но быстро разваливается при небольшом количестве потоков.

  • Повышение производительности.Дизайн потока на соединение просто не масштабируется.См. Проблему C10K .

  • Композиция (или Цепочка).Операции более высокого уровня могут состоять из нескольких обработчиков завершения.Рассмотрите возможность передачи изображения JPEG, протокол может диктовать, что первые 40 байтов включают заголовок, описывающий размер изображения, форму, возможно, некоторую другую информацию.Первый обработчик завершения для отправки этого заголовка может инициировать вторую операцию для отправки данных изображения.Операция более высокого уровня sendImage() не должна знать или заботиться о цепочке методов, используемых для осуществления передачи данных.

  • Время ожидания и возможность отмены.Существуют специфичные для платформы способы тайм-аута длительной работы (например: SO_RCVTIMEO и SO_SNDTIMEO).Использование асинхронных операций позволяет использовать deadline_timer для отмены длительных операций на всех поддерживаемых платформах.


Конечно, в обеих операциях всегда существует риск того, чтоПри некоторых обстоятельствах поток программы останавливается на неопределенный срок - здесь используются таймеры, но я хотел бы узнать некоторые более авторитетные мнения по этому вопросу.

Мой личный опыт использования Asio связан с аспектом масштабируемости.Написание программного обеспечения для суперкомпьютеров требует значительных усилий при работе с ограниченными ресурсами, такими как память, потоки, сокеты и т. Д. Использование потока на соединение для ~ 2 миллионов одновременных операций является мертвым проектомпо прибытии.

3 голосов
/ 12 марта 2011

Полагаю, выбор синхронного / асинхронного зависит от конкретного приложения.Я согласен, что асинхронная парадигма может сделать код, а также отладку намного более сложным, но у него есть свои преимущества.

Чтобы проиллюстрировать, основная причина, по которой мы переключились с синхронного ввода-вывода на повышение asio с помощью асинхронного ввода-вывода, заключается в том, что в нашем приложении блокировка ввода-вывода была просто невозможной, у нас есть сервер потоковой передачи мультимедиа, на котором я выполнял потоковую передачу мультимедийных пакетов.нескольким клиентам после кодирования.Проблема заключалась в том, что из-за проблем с сетью весь конвейер захвата-кодирования-доставки был фактически остановлен (например, при сбое подключения к одному клиенту).

Подводя итог, в моем (ltd) опыте с асинхронным вводом-выводом,это может быть полезно в ситуациях, когда у вас есть другая работа, которую необходимо выполнить, пока вы ожидаете завершения ввода-вывода (например, обслуживание других клиентов и т. д.).В системах или сценариях, где вам приходится ждать продолжения ввода-вывода, было бы гораздо проще использовать синхронный ввод-вывод.

Это также имело бы смысл в системах дуплексной связи (например, в более сложных протоколах).такие как SIP, RTSP, где и клиент, и сервер могут отправлять запросы).Прошло много времени с тех пор, как я имел дело с POP, но для простого обмена в вашем примере асинхронный ввод-вывод можно считать излишним.Я бы переключился на асинхронный ввод-вывод, только когда был уверен, что синхронизации ввода-вывода недостаточно для удовлетворения моих требований.

WRT к документации boost asio, я обнаружил, что лучший способ освоить его - этопроработайте примеры.Кроме того, ссылка, которую вы, возможно, захотите проверить: http://en.highscore.de/cpp/boost/index.html Там есть действительно хорошая глава по boost asio.Кроме того, блог Криса Колхоффа (автора asio) содержит несколько действительно хороших статей, достойных внимания.

2 голосов
/ 16 июля 2018

синхронный легко контролировать поток программы.

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

асинхронный использует обратный вызов и трудно программист. Мы можем попробовать обещание-cpp сделать асинхронный поток похожим на синхронный -

Пример http-клиента -

//<1> Resolve the host
async_resolve(session->resolver_, host, port)

.then([=](tcp::resolver::results_type &results) {
    //<2> Connect to the host
    return async_connect(session->socket_, results);

}).then([=]() {
    //<3> Write the request
    return async_write(session->socket_, session->req_);

}).then([=](std::size_t bytes_transferred) {
    boost::ignore_unused(bytes_transferred);
    //<4> Read the response
    return async_read(session->socket_, session->buffer_, session->res_);

}).then([=](std::size_t bytes_transferred) {
    boost::ignore_unused(bytes_transferred);
    //<5> Write the message to standard out
    std::cout << session->res_ << std::endl;

}).then([]() {
    //<6> success, return default error_code
    return boost::system::error_code();
}, [](const boost::system::error_code err) {
    //<6> failed, return the error_code
    return err;

}).then([=](boost::system::error_code &err) {
    //<7> Gracefully close the socket
    std::cout << "shutdown..." << std::endl;
    session->socket_.shutdown(tcp::socket::shutdown_both, err);
});

Полный код здесь

0 голосов
/ 29 июля 2013

Простой ответ: (цитата из здесь с небольшими изменениями)

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

В ASync все происходит одновременно. Обычно многопоточные приложения, которые будут выполнять несколько задач одновременно.

...