Asynchronous Programming in C++: A Complete Guide with Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Asynchronous Programming in C++: from std::async to Coroutines



Asynchronous programming in C++ is a powerful tool for creating high-performance and responsive applications. In the modern world, where multi-core processors have become the standard, the ability to efficiently manage parallel tasks is a mandatory skill for every C++ developer. In this article, we will break down key concepts, starting with the basic mechanisms of the standard library and ending with modern coroutines from C++20.



Why is asynchronous programming needed?

The main goal of asynchronous programming is not to block the main execution thread of the program. Imagine your application needs to download a file from the network, process it, and show it to the user. If you perform these operations synchronously, the interface will "freeze" during the download. The asynchronous approach allows you to run a long operation "in the background," while the main thread continues to handle user input.



Basic approaches to asynchrony in C++

In C++, there are several ways to implement asynchronous execution:

  • Manual thread management (std::thread) — a low-level but flexible approach.
  • std::async and std::future — a high-level mechanism for launching asynchronous tasks.
  • std::packaged_task and std::promise — for finer control.
  • Coroutines (C++20) — a modern and elegant way to write asynchronous code.


Using std::async and std::future

This is the simplest way to launch an asynchronous task. The std::async function returns a std::future object, which will contain the result of the execution.



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

int heavy_computation(int x) { std::this_thread::sleep_for(std::chrono::seconds(2)); return x * x;}

int main() { std::cout << "Launching asynchronous task..."; std::future<int> result = std::async(std::launch::async, heavy_computation, 42); // Do something else while the task is running std::cout << "Main thread is not blocked!"; // Get the result (blocks if the task is not yet complete) int value = result.get(); std::cout << "Result: " << value << ""; return 0;}


The std::launch::async flag guarantees that the task will be executed in a separate thread. If it is not specified, the implementation may defer execution until .get() is called.



Working with std::promise and std::packaged_task

These mechanisms provide more control. std::promise allows you to "promise" a value that will be set later, while std::packaged_task wraps a callable object into a task.



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

void set_value(std::promise<int>&& prom) { std::this_thread::sleep_for(std::chrono::seconds(1)); prom.set_value(100);}

int main() { std::promise<int> prom; std::future<int> fut = prom.get_future(); std::thread t(set_value, std::move(prom)); std::cout << "Waiting for result..."; int value = fut.get(); std::cout << "Received: " << value << ""; t.join(); return 0;}


Important: std::promise and std::future are move-only objects. They cannot be copied, only moved.



Coroutines in C++20: a new level of asynchrony

Coroutines are functions that can suspend their execution and resume it later. They allow you to write asynchronous code as if it were synchronous.



Basic

Blogs

Book Recommendations