Introduction to Asynchronous Programming in C++
Modern applications increasingly require high performance and responsiveness. Asynchronous programming in C++ is a powerful tool that allows you to execute multiple tasks simultaneously without blocking the main execution thread. In this guide, we will cover the basics of asynchrony in C++: from classical approaches to modern coroutines added in the C++20 standard.
If you are new to C++ but already familiar with basic syntax, this article will help you understand how to make your program do several things at once. We will look at practical examples that you can immediately apply in your own projects.
Core Concepts and Why You Need This
In traditional synchronous programming, code executes strictly sequentially: one operation completes, then the next one begins. This is simple but inefficient if the program is waiting for input/output (reading a file, network request) or performing heavy computations.
Asynchronous programming solves this problem by allowing:
- Not blocking the main thread — the application interface remains responsive.
- Speeding up execution — parallel data processing on multi-core processors.
- Efficient resource usage — while one task waits for data, another can execute.
In C++, there are several ways to implement asynchrony: from low-level threads (std::thread) to high-level abstractions (std::async, std::future, and coroutines).
1. Asynchrony with std::async and std::future
The simplest way to launch an asynchronous task in C++ is to use the std::async function from the <future> header. It returns a std::future object that stores the result of execution.
Basic Example
#include <iostream>#include <future>#include <thread>#include <chrono>
// Function to be executed asynchronouslyint slow_calculation(int x) { std::this_thread::sleep_for(std::chrono::seconds(2)); // Simulating long work return x * x;}
int main() { std::cout << "Launching asynchronous task..." << std::endl; // Launch slow_calculation in a separate thread std::future<int> result = std::async(std::launch::async, slow_calculation, 5); std::cout << "Main thread continues working..." << std::endl; // Get the result (blocks the thread if the task is not yet complete) int value = result.get(); std::cout << "Result: " << value << std::endl; // Output: 25 return 0;}In this example, std::async launches the function in a separate thread. While it executes, the main thread prints a message. The get() method waits for the task to complete and returns the result.
Launch Policies
std::async has two modes:
std::launch::async— the task is guaranteed to execute in a new thread.std::launch::deferred— the task executes lazily, only whenget()orwait()is called in the same thread.
2. Promises and Futures: Manual Management
If you need more flexible control over asynchronous tasks, use the std::promise and std::future pair. promise allows you to set a value or exception in one thread, while future allows you to retrieve it in another.
Example with promise
#include <iostream>#include <future>#include <thread>
void worker(std::promise<int> prom, int a, int b) { try { int result = a / b; // May