В чем разница между std::async и std::thread в C++?

«В чем разница между std::async и std::thread в C++?» — вопрос из категории Многопоточность, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

std::thread — это низкоуровневый примитив для создания и управления потоком. Он требует явного управления жизненным циклом потока (вызов join() или detach()).

std::async — это высокоуровневая абстракция для запуска асинхронных задач. Она возвращает std::future<T> и делегирует управление потоками стандартной библиотеке (используя внутренний пул потоков или создавая новый).

Ключевые различия:

  1. Управление потоком:

    • std::thread создает поток немедленно.
    • std::async может отложить выполнение (ленивая политика std::launch::deferred) или запустить асинхронно (std::launch::async).
  2. Возврат результата и обработка ошибок:

    • std::thread не предоставляет прямого способа получить результат работы функции. Для передачи данных или ошибок нужны дополнительные механизмы (например, общие переменные с синхронизацией).
    • std::async инкапсулирует результат (или исключение) в объект std::future. Исключение, выброшенное в асинхронной задаче, будет перевыброшено при вызове future.get().
  3. Производительность и ресурсы:

    • Создание std::thread — относительно дорогая операция.
    • std::async может использовать существующие потоки из внутреннего пула, что эффективнее для множества мелких задач.

Пример:

// Пример с std::thread (нужно вручную передать результат)
int result;
std::thread t([&result]() { result = someHeavyCalculation(); });
t.join();
std::cout << result << std::endl;

// Пример с std::async (результат инкапсулирован)
auto future = std::async(std::launch::async, []() {
    return someHeavyCalculation();
});
// ... делаем другую работу ...
std::cout << future.get() << std::endl; // Блокируется, пока результат не готов

Когда что использовать:

  • std::thread — когда нужен полный низкоуровневый контроль над потоком (например, привязка к конкретному ядру CPU, нестандартный стек).
  • std::async — для большинства задач «запусти-и-забудь» с получением результата, так как это безопаснее и часто эффективнее.