Обезличенный производственный кейс · C++

AsyncTaskManager

Подсистема отделяет длительные операции от клиентского запроса и выполняет их в пуле рабочих потоков.

Среда
Рабочий C++-сервис
Роль
Проектирование и реализация
Основа
Очередь, пул потоков, состояния задач
Синхронизация
mutex, condition_variable
Очередь задач, состояния и пул рабочих потоков

Контекст

Клиентский запрос не должен ждать длительную операцию

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

Отдельный запуск

Клиент запускает операцию и сразу получает номер задачи.

Отдельная проверка

Состояние можно запросить позже по номеру задачи.

Выполнение в потоках

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

Явные состояния

Задача проходит состояния «в очереди», «выполняется» и одно из конечных состояний.

Выполнение

Блокировка защищает очередь, а не саму работу

Рабочий поток ждёт на условной переменной, под блокировкой извлекает одну задачу и меняет её состояние. После этого блокировка снимается, и начинается длительная операция. Если выполнять её под тем же mutex, остальные потоки не смогут брать задачи и пул фактически станет однопоточным.

Запуск задачи и её выполнение вне блокировки очереди
Под блокировкой меняются только очередь и состояние; длительная работа выполняется после освобождения mutex.

Остановка

При остановке нужно разбудить и ожидающие потоки

Условие ожидания реагирует и на появление задачи, и на запрос остановки. Поток завершает работу, когда остановка уже запрошена, а очередь пуста. Это даёт осторожное поведение: перестать принимать новые задачи, закончить уже принятые и затем дождаться завершения рабочих потоков.

Такая политика остановки восстановлена по устройству синхронизации компонента. На странице не утверждается, что в рабочей системе происходили взаимная блокировка или потеря задач.

Ошибки

Ошибка одной операции не должна останавливать рабочий поток

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

Оценка рабочих показателей

Запрос завершается быстро, а работа продолжается в пуле

Исходные замеры не сохранились. Диапазоны ниже — осторожная оценка для очереди в памяти и описанного класса длительных операций, а не результаты проверки производительности.

Приём задачи

10–50 мс

Ожидаемое время на проверку запроса, помещение задачи в очередь и возврат её номера.

Длительность операции

5 с–несколько мин

Примерный диапазон, в котором отделение операции от клиентского запроса становится полезным.

Параллельное выполнение

4–8 потоков

Реалистичный размер пула для смешанных вычислительных операций и ввода-вывода; остальные задачи ждут в очереди.

Ожидание клиента

в 100+ раз меньше

Для операций длительностью в несколько секунд возврат только номера задачи сокращает время запроса примерно на два порядка.

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

Результат

Время запроса больше не связано со временем операции

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

Следующий проект

Помощник по резюме

Локальное приложение для подготовки откликов без передачи закрытых данных модели.

Читать кейс →