Асинхронное программирование в C++: полное руководство для начинающих

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в асинхронное программирование на C++

Современные приложения все чаще требуют высокой производительности и отзывчивости. Асинхронное программирование в C++ — это мощный инструмент, который позволяет выполнять несколько задач одновременно, не блокируя основной поток выполнения. В этом руководстве мы разберем основы асинхронности на C++: от классических подходов до современных корутин (coroutines), добавленных в стандарт C++20.

Если вы новичок в C++, но уже знакомы с базовым синтаксисом, эта статья поможет вам понять, как заставить вашу программу делать несколько дел одновременно. Мы рассмотрим практические примеры, которые можно сразу применить в своих проектах.

Основные концепции и зачем это нужно

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

Асинхронное программирование решает эту проблему, позволяя:

  • Не блокировать основной поток — интерфейс приложения остается отзывчивым.
  • Ускорять выполнение — параллельная обработка данных на многоядерных процессорах.
  • Эффективно использовать ресурсы — пока одна задача ждет данные, другая может выполняться.

В C++ существует несколько способов реализовать асинхронность: от низкоуровневых потоков (std::thread) до высокоуровневых абстракций (std::async, std::future и корутины).

1. Асинхронность с std::async и std::future

Самый простой способ запустить асинхронную задачу в C++ — использовать функцию std::async из заголовочного файла <future>. Она возвращает объект std::future, который хранит результат выполнения.

Базовый пример

#include <iostream>
#include <future>
#include <thread>
#include <chrono>

// Функция, которую будем выполнять асинхронно
int slow_calculation(int x) {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // Имитация долгой работы
    return x * x;
}

int main() {
    std::cout << "Запускаем асинхронную задачу..." << std::endl;
    
    // Запускаем slow_calculation в отдельном потоке
    std::future<int> result = std::async(std::launch::async, slow_calculation, 5);
    
    std::cout << "Основной поток продолжает работу..." << std::endl;
    
    // Получаем результат (блокирует поток, если задача еще не выполнена)
    int value = result.get();
    std::cout << "Результат: " << value << std::endl; // Вывод: 25
    
    return 0;
}

В этом примере std::async запускает функцию в отдельном потоке. Пока она выполняется, основной поток выводит сообщение. Метод get() ожидает завершения задачи и возвращает результат.

Политики запуска

У std::async есть два режима:

  • std::launch::async — задача гарантированно выполняется в новом потоке.
  • std::launch::deferred — задача выполняется лениво, только при вызове get() или wait() в том же потоке.

2. Promises и Futures: ручное управление

Если вам нужно более гибкое управление асинхронными задачами, используйте пару std::promise и std::future. promise позволяет установить значение или исключение в одном потоке, а future — получить его в другом.

Пример с promise

#include <iostream>
#include <future>
#include <thread>

void worker(std::promise<int> prom, int a, int b) {
    try {
        int result = a / b; // Может выбросить исключение при делении на ноль
        prom.set_value(result);
    } catch(...) {
        prom.set_exception(std::current_exception());
    }
}

int main() {
    std::promise<int> prom;
    std::future<int>> fut = prom.get_future();
    
    std::thread t(worker, std::move(prom), 10, 0); // Передаем promise в поток
    
    try {
        int result = fut.get();
        std::cout << "Результат: " << result << std::endl;
    } catch(const std::exception& e) {
        std::cout << "Исключение: " << e.what() << std::endl;
    }
    
    t.join();
    return 0;
}

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

3. Корутины в C++20 (Coroutines)

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

Простейшая корутина

Для работы с корутинами нужен компилятор с поддержкой C++20 (например, GCC 11+, Clang 14+, MSVC 2022). Вот минимальный пример:

#include <iostream>
#include <coroutine>
#include <optional>

// Простой генератор чисел
struct Generator {
    struct promise_type {
        int current_value;
        
        Generator get_return_object() {
            return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        std::suspend_always yield_value(int value) {
            current_value = value;
            return {};
        }
        void return_void() {}
        void unhandled_exception() { std::exit(1); }
    };
    
    std::coroutine_handle<promise_type> handle;
    
    Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
    ~Generator() { if (handle) handle.destroy(); }
    
    bool next() {
        if (!handle.done()) {
            handle.res                

Блоги

Рекомендаций по книгам