Introduction to Asynchronous Programming in C++
\n\nIn the modern world of software development, application performance and responsiveness play a key role. Asynchronous programming in C++ allows you to efficiently use system resources by executing multiple tasks simultaneously without blocking the main thread. This is especially important when working with network requests, input/output operations, and long-running computations.
\n\nUnlike the traditional synchronous approach, where each operation is executed sequentially, asynchronous code can run tasks in the background and retrieve results as they become ready. In this guide, we will explore the main tools of asynchronous programming in C++, starting with simple concepts and ending with the modern capabilities of the C++20 standard.
\n\nAsynchrony Basics: std::async and std::future
\n\nThe simplest way to get started with asynchronous programming in C++ is to use std::async. This function launches a task asynchronously and returns a std::future object that contains the result of the execution.
Basic example with std::async
\n\n#include <iostream>\n#include <future>\n#include <chrono>\n\nint long_operation() {\n std::this_thread::sleep_for(std::chrono::seconds(2));\n return 42;\n}\n\nint main() {\n std::cout << "Launching asynchronous task..." << std::endl;\n \n // Launching an asynchronous task\n std::future<int> result = std::async(std::launch::async, long_operation);\n \n std::cout << "Main thread continues working..." << std::endl;\n \n // Getting the result (blocks the thread until ready)\n int value = result.get();\n std::cout << "Result: " << value << std::endl;\n \n return 0;\n}\n\nThe launch policy std::launch::async guarantees that the function will be executed in a separate thread. An alternative is std::launch::deferred, which postpones execution until get() is called.
Working with multiple tasks
\n\nAsynchronous programming is especially effective when executing several independent tasks in parallel:
\n\n#include <iostream>\n#include <future>\n#include <vector>\n\nint compute_square(int x) {\n return x * x;\n}\n\nint main() {\n std::vector<std::future<int>> tasks;\n \n // Launching several asynchronous tasks\n for (int i = 1; i <= 5; ++i) {\n tasks.push_back(std::async(std::launch::async, compute_square, i));\n }\n \n // Collecting results\n for (auto& task : tasks) {\n std::cout << "Result: " << task.get() << std::endl;\n }\n \n return 0;\n}\n\nAdvanced Tools: std::promise and std::packaged_task
\n\nFor more flexible management of asynchronous operations in C++, std::promise and std::packaged_task are provided. These tools allow you to pass values between threads and wrap callable objects.
Using std::promise
\n\nstd::promise allows you to set a value that will subsequently be retrieved through the corresponding std::future object:
#include <iostream>\n#include <future>\n#include <thread>\n\nvoid producer_thread(std::promise<std::string> promise) {\n try {\n std::string data = "Important data from thread";\n // Setting the value\n promise.set_value(data);\n } catch(...) {\n // Exception handling\n promise.set_exception(std::current_exception());\n }\n}\n\nint main() {\n std::promise<std::string> promise;\n std::future<std::string> future = promise.get_future();\n \n std::thread thread(pr