Начало. Библиотека Asio предлагает поддержку синхронных и асинхронных операций. Асинхронная поддержка основана на шаблоне проектирования Proactor.[POSA2]. Преимущества и недостатки этого подхода по сравнению с синхронным или реакторным подходом описаны ниже.
Давайте рассмотрим, как схема проектирования Проактора реализована в Boost. Asio, без упоминания платформенных деталей.

Модель конструкции реактора (адаптированная из [POSA2])
#8212 Асинхронная операция
Определяет операцию, которая выполняется асинхронно, такую как асинхронное чтение или запись на розетке.
#8212 Процессор асинхронной операции
Выполняет асинхронные операции и очередей событий в очереди событий завершения, когда операции завершены. С точки зрения высокого уровня, такие службы, как<stream_socket_service
>, являются асинхронными операционными процессорами.
#8212 - Завершение очередей событий
Буферы завершают события до тех пор, пока они не будут очерчены асинхронным демультиплексером событий.
#8212 - Завершение очередей событий
Обрабатывает результат асинхронной операции. Это функциональные объекты, часто создаваемые с помощью<boost::bind
>.
#8212 Асинхронное событие Demultiplexer
Блокирует ожидание событий, которые должны произойти в очереди завершения события, и возвращает завершенное событие своему абоненту.
#8212 Проактор
Призывает демультиплексор асинхронного события к событиям очереди и отправляет обработчик завершения (то есть вызывает объект функции), связанный с событием. Эта абстракция представлена классом<io_service
>.
#8212 - инициатор
Специфический код приложения, который запускает асинхронные операции. Инициатор взаимодействует с асинхронным операционным процессором через интерфейс высокого уровня, такой как<basic_stream_socket
>, который, в свою очередь, делегирует такой сервис, как<stream_socket_service
>.
На многих платформах, скачайте. Asio реализует схему проектирования Проактора с точки зрения Реактора, такого как<select
>,<epoll
>или<kqueue
>. Этот подход к реализации соответствует схеме проектирования Проактора следующим образом:
#8212 Процессор асинхронной операции
Реактор, реализуемый с использованием<select
>,<epoll
>или<kqueue
>. Когда реактор указывает, что ресурс готов к выполнению операции, процессор выполняет асинхронную операцию и ставит в очередь соответствующий обработчик завершения в очереди завершения.
#8212 - Завершение очередей событий
Связанный список обработчиков завершения (т.е. функциональных объектов).
#8212 Асинхронное событие Demultiplexer
Это реализуется путем ожидания переменной события или состояния, пока обработчик завершения не будет доступен в очереди завершения события.
Windows NT, 2000 и XP, Boost. Asio использует преимущества перекрытого ввода-вывода, чтобы обеспечить эффективную реализацию шаблона проектирования Proactor. Этот подход к реализации соответствует схеме проектирования Проактора следующим образом:
#8212 Процессор асинхронной операции
Это реализуется операционной системой. Операции инициируются вызовом перекрывающейся функции, такой как<AcceptEx
>.
#8212 - Завершение очередей событий
Это реализуется операционной системой и связано с портом завершения ввода/вывода. Существует один порт завершения ввода/вывода для каждого<io_service
>экземпляра.
#8212 Асинхронное событие Demultiplexer
Называется Boost. Asio для очередей событий и связанных с ними обработчиков завершения.
#8212 Портативность.
Многие операционные системы предлагают собственный асинхронный API ввода/вывода (например, перекрывающийся I/O).Windowsкак предпочтительный вариант для разработки высокопроизводительных сетевых приложений. Библиотека может быть реализована с точки зрения нативного асинхронного ввода/вывода. Однако, если нативная поддержка недоступна, библиотека также может быть реализована с использованием синхронных демультиплексоров событий, которые типизируют шаблон реактора, такой какPOSIX<select()
>.
— Разъединение потоков от параллелизма.
Долгосрочные операции выполняются асинхронно реализацией от имени приложения. Следовательно, приложениям не нужно создавать много потоков, чтобы увеличить параллель.
— Эффективность и масштабируемость.
Стратегии реализации, такие как подключение по потоку (что потребует синхронного подхода), могут ухудшить производительность системы из-за увеличения переключения контекста, синхронизации и перемещения данных между процессорами. При асинхронных операциях можно избежать затрат на переключение контекста, минимизируя количество потоков операционной системы — обычно ограниченный ресурс — и только активируя логические потоки управления, которые имеют события для обработки.
#8212 Упрощенная синхронизация приложений.
Обработчики асинхронного завершения операции могут быть написаны так, как если бы они существовали в однопоточной среде, и поэтому логика приложения может быть разработана с минимальным беспокойством о проблемах синхронизации.
— Функциональная композиция.
Функциональная композиция относится к реализации функций для обеспечения операции более высокого уровня, такой как отправка сообщения в определенном формате. Каждая функция реализована в виде нескольких вызовов для операций чтения или записи на более низком уровне.
Например, рассмотрим протокол, в котором каждое сообщение состоит из заголовка фиксированной длины, за которым следует корпус переменной длины, где длина корпуса указана в заголовке. Гипотетическая операция read_message может быть реализована с использованием двух низкоуровневых считываний: первый получает заголовок, а второй, как только длина известна, получает тело.
Для составления функций в асинхронной модели асинхронные операции могут быть связаны друг с другом. То есть обработчик завершения одной операции может инициировать следующую. Запуск первого вызова в цепочке может быть инкапсулирован таким образом, чтобы абонент не знал, что операция более высокого уровня реализована как цепочка асинхронных операций.
Возможность составления новых операций таким образом упрощает разработку более высоких уровней абстракции над сетевой библиотекой, таких как функции для поддержки конкретного протокола.
#8212 - сложность программы.
Более сложно разрабатывать приложения с использованием асинхронных механизмов из-за разделения во времени и пространстве между началом и завершением операции. Приложения также могут быть более сложными для отладки из-за перевернутого потока управления.
#8212 Использование памяти.
Буферное пространство должно быть выполнено на время операции чтения или записи, которая может продолжаться бесконечно, и для каждой одновременной операции требуется отдельный буфер. С другой стороны, модель реактора не требует буферного пространства, пока розетка не будет готова к чтению или письму.
[POSA2] Д. Шмидт и др.Pattern Oriented Software Architecture, Volume 2. Уайли, 2000.