1.23M

Tema_11_Async_IO

1.

ТЕМА 11
Асинхронная
обработка
данных
Концепция асинхронного ввода-вывода
и порты завершения
Операционные системы
01

2.

Работа продолжается во время ожидания
Запрос сейчас.
Результат позже.
1 Запуск операции
Приложение передаёт
дескриптор, буфер и контекст.
2 ОС выполняет запрос
Асинхронный ввод-вывод позволяет запустить
операцию и получить её результат отдельно
от вызова, который её начал.
Поток может обрабатывать
другую работу.
3 Приходит завершение
Приложение проверяет статус и
использует данные.
02

3.

Три модели ввода-вывода
Модель
Что получает вызывающий поток
Как продолжить работу
Блокирующая
Ждёт результат внутри вызова
После возврата из read /
ReadFile
Неблокирующая
Быстрый ответ: данные или «пока
нет»
Ждать готовности и повторить
попытку
Асинхронная
Статус запуска операции
Получить уведомление о её
завершении
Готовность к I/O отличается от завершения операции.
03

4.

Перекрытие ожидания и вычислений
Учебный пример: I/O занимает 100 мс, независимые вычисления — 20 мс.
120 мс
120
Время, мс
Последовательно
С перекрытием
100
100 + 20
≈ 100 мс
max(100, 20)
Условия: работа независима, перекрытие возможно, накладные расходы не учитываются.
04

5.

Как приложение узнаёт о завершении
Механизм
Как устроено уведомление
Где полезен
Событие (event)
ОС сигнализирует объект события
Небольшое число ожидаемых
операций
Completion routine
Callback при alertable wait
Последовательность в
заданном потоке
Опрос результата
Программа сама проверяет состояние
Когда частота проверки
оправдана
Порт завершения
IOCP
Пакеты завершений в общей очереди
Много операций и рабочие
потоки
05

6.

OVERLAPPED: контекст одной операции
Дескриптор
Файл открывают с
FILE_FLAG_OVERLAPPED.
Для каждой активной операции создают
отдельный контекст.
struct Request {
OVERLAPPED ov{};
char buffer[4096];
// данные запроса
};
OVERLAPPED
Буфер и контекст
Смещение в файле, служебные поля и
необязательный hEvent.
Живут до подтверждённого завершения
операции.
06

7.

Три результата запуска ReadFile
Возврат ReadFile
Значение
Действие приложения
TRUE
Операция завершилась сразу
При обычном IOCP ждать пакет
завершения
FALSE +
ERROR_IO_PENDING
Операция принята и ещё
выполняется
Сохранить буфер и контекст,
ждать завершения
FALSE +
другая ошибка
Запуск не принят либо вернул
ошибку
Обработать код ошибки запуска
ERROR_IO_PENDING — штатный статус асинхронного запроса.
07

8.

Порт завершения ввода-вывода
IOCP
I/O Completion Port
Объект ядра Windows с очередью
пакетов завершения и механизмом
пробуждения рабочих потоков.
01
Дескрипторы
Файлы, сокеты и каналы связаны с
портом.
02
Операции
Приложение запускает overlapped I/O.
03
Очередь завершений
ОС добавляет сведения о
результатах.
04
Рабочие потоки
Извлекают пакеты и продолжают
обработку.
08

9.

Основные API Windows
Этап
Функция
Назначение
Создание и привязка
CreateIoCompletionPort
Создать порт и связать с ним
дескриптор
Запуск I/O
ReadFile / WriteFile
WSARecv / WSASend
Передать запрос вместе с
OVERLAPPED
Получение результата
GetQueuedCompletionStatus
Извлечь очередной пакет
завершения
Служебные сообщения
PostQueuedCompletionStatus
Добавить собственный пакет в
очередь
09

10.

Содержимое пакета завершения
Поле результата
Что позволяет узнать
Число переданных байтов
Сколько данных фактически обработала операция
CompletionKey
К какому дескриптору или соединению относится
результат
OVERLAPPED*
Какая именно операция завершилась
Статус операции
Успех либо код ошибки после возврата функции
Ключ описывает дескриптор. OVERLAPPED различает запросы к нему.
10

11.

Рабочие потоки и конкурентность
Много запросов
ожидают I/O
Ограниченное число
потоков
обрабатывает
завершения
Предел конкурентности
Короткие обработчики
IOCP ограничивает число одновременно
работающих потоков, связанных с портом.
Долгие вычисления и блокирующие
вызовы задерживают обработку
следующих пакетов.
11

12.

Пример: запуск чтения с IOCP
auto* req = new Request{};
BOOL ok = ReadFile(file, req->buffer,
sizeof(req->buffer), nullptr, &req->ov);
if (!ok) {
DWORD err = GetLastError();
if (err != ERROR_IO_PENDING) {
report_error(err);
delete req; // запрос не принят
}
}
// TRUE или PENDING: req обработает рабочий поток IOCP.
Фрагмент C++: файл уже связан с IOCP, уведомления о завершении включены.
12

13.

Пример: получение завершения
DWORD bytes = 0;
ULONG_PTR key = 0;
OVERLAPPED* ov = nullptr;
BOOL ok = GetQueuedCompletionStatus(
port, &bytes, &key, &ov, INFINITE);
DWORD err = ok ? ERROR_SUCCESS : GetLastError();
if (ov != nullptr) {
finish_request(ov, bytes, key, err);
} else if (!ok) {
handle_wait_error(err); // key и bytes не читать
} else {
handle_control_packet(key);
}
FALSE + ov ≠ nullptr: операция завершилась с ошибкой, и её контекст нужно обработать.
13

14.

Срок жизни запроса и отмена
1
Создание
Буфер и OVERLAPPED готовы до запуска.
2
Выполнение
Приложение сохраняет контекст и не меняет буфер.
3
Отмена
Если нужна отмена, приложение вызывает CancelIoEx.
4
Подтверждение
Приложение получает итоговое завершение.
5
Освобождение
После обработки результата ресурсы можно вернуть.
CancelIoEx не дожидается завершения отмены.
14

15.

Ограничение нагрузки и память
N×B
Активные запросы × размер буфера
10 000 × 64 КиБ ≈ 625 МиБ
Только буферы, без контекстов и памяти ОС.
Расчётный пример, не измерение.
Лимит активных операций
При достижении лимита
приложение приостанавливает
новые запросы.
Контроль очередей
Backpressure согласует скорость
поступления и обработки данных.
Измерение результата
Время ответа, скорость обработки,
память и загрузка CPU.
15

16.

Сценарий: сетевой сервер
01
Сервер размещает WSARecv для соединения.
02
Пока данные идут по сети, поток свободен.
03
IOCP сообщает о завершении приёма.
04
Обработчик разбирает данные и формирует
ответ.
05
WSASend запускает отправку ответа.
16

17.

Windows и Linux: готовность и завершение
Механизм
Событие
Что делает приложение
Windows IOCP
Операция завершилась
Извлекает пакет и обрабатывает
результат
Linux epoll
Дескриптор готов к
операции
Пробует неблокирующее чтение
или запись
Linux io_uring
Результат отправленного
запроса
Получает CQE из очереди
завершений
epoll обычно связывают с Reactor, IOCP и io_uring — с моделью завершений.
17

18.

Ошибки, которые нарушают работу I/O
Ошибка
Последствие
Правильный подход
Освободить буфер после
запуска
Обращение к уже
освобождённой памяти
Дождаться итогового
завершения
Переиспользовать
OVERLAPPED
Смешение контекстов
запросов
Отдельная структура на
активную операцию
Обработать TRUE дважды
Повторная обработка или
освобождение
Один согласованный путь
завершения
Ожидать порядок запуска
Неверное состояние
приложения
Учитывать независимый
порядок завершений
18

19.

Главные выводы и самопроверка
Асинхронность
Разделяет запуск I/O и получение
результата.
IOCP
Объединяет завершения многих
запросов в одну очередь.
Объясните своими
словами
1. Почему ERROR_IO_PENDING
не означает сбой?
2. Чем готовность отличается
от завершения?
3. Когда можно освободить
буфер после CancelIoEx?
Надёжность зависит от управления
контекстами, буферами и нагрузкой.
19

20.

Источники для изучения
01
Microsoft Learn
Synchronous and Asynchronous I/O
02
Microsoft Learn
I/O Completion Ports
03
Microsoft Learn
ReadFile и OVERLAPPED
04
Microsoft Learn
GetQueuedCompletionStatus и CancelIoEx
05
Linux man-pages
epoll(7)
06
Linux man-pages
io_uring(7)
Названия источников кликабельны. Точные ссылки к темам приведены в заметках слайдов.
20
English     Русский Правила