YouTip LogoYouTip

Cpp Libs Future

# C++ Standard Library: `` Introduced in C++11, the `` header provides a robust mechanism for asynchronous programming. It allows a program to execute tasks concurrently and retrieve their results at a later point without blocking the main execution thread unnecessarily. The `` library is a fundamental component of C++ concurrency, offering a high-level, safe, and clean abstraction over raw threads. --- ## Key Components The `` library defines several core classes and functions to manage asynchronous state and task execution: * **`std::future`**: Represents the read-end of an asynchronous channel. It is used to query the status of an asynchronous operation, wait for it to complete, and retrieve its result. * **`std::shared_future`**: Similar to `std::future`, but allows multiple threads to wait for and retrieve the same asynchronous result (since `std::future` is move-only and can only have its result retrieved once). * **`std::promise`**: Represents the write-end of an asynchronous channel. It is used by a provider thread to set the value or exception that will be consumed by a `std::future`. * **`std::packaged_task`**: Wraps any callable target (function, lambda, functor) so that it can be executed asynchronously, automatically linking its return value or thrown exception to a `std::future`. * **`std::async`**: A high-level helper function used to run a callable asynchronously, returning a `std::future` immediately. --- ## 1. `std::promise` and `std::future` `std::promise` and `std::future` work in pairs. The `std::promise` object is used by the producer thread to set a value, and the `std::future` object is used by the consumer thread to retrieve that value. ### Code Example ```cpp #include #include #include int main() { // Create a promise to hold an integer value std::promise prom; // Get the associated future from the promise std::future fut = prom.get_future(); // Pass the promise to a worker thread (must be moved) std::thread t([&prom]() { // Simulate some work, then set the value prom.set_value(10); }); // Wait for the value to become available and print it std::cout << "Future value: " << fut.get() << std::endl; // Join the thread t.join(); return 0; } ``` ### Output ```text Future value: 10 ``` --- ## 2. `std::packaged_task` `std::packaged_task` wraps a callable object and packages it so that its result can be retrieved via a `std::future`. It is particularly useful when building custom thread pools or task schedulers. ### Code Example ```cpp #include #include #include #include // A simple function to compute square root double compute_square_root(double x) { return std::sqrt(x); } int main() { // Package the function std::packaged_task task(compute_square_root); // Get the future associated with the task std::future result = task.get_future(); // Move the task into a thread and execute it with an argument std::thread th(std::move(task), 9.0); // Retrieve and print the result (blocks until the thread finishes) std::cout << "Result: " << result.get() << std::endl; th.join(); return 0; } ``` ### Output ```text Result: 3 ``` --- ## 3. `std::async` `std::async` is the simplest and most common way to run a task asynchronously. It automatically manages thread creation and returns a `std::future` containing the result of the callable. You can specify a launch policy: * `std::launch::async`: Guarantees that the task runs on a separate, newly spawned thread. * `std::launch::deferred`: Lazy evaluation. The task runs on the calling thread only when `get()` or `wait()` is called on the returned future. ### Code Example ```cpp #include #include int main() { // Launch a lambda asynchronously std::future fut = std::async(std::launch::async, [](int x) { return x * x; }, 5); // Do other work here... // Retrieve the result std::cout << "Result: " << fut.get() << std::endl; return 0; } ``` ### Output ```text Result: 25 ``` --- ## Exception Handling If an asynchronous task throws an exception, that exception is captured by the shared state and stored. When the consumer calls `std::future::get()`, the stored exception is rethrown in the calling thread. This ensures that errors in background threads do not go unnoticed. ### Code Example ```cpp #include #include #include void throw_exception() { throw std::runtime_error("Exception thrown from background task"); } int main() { // Run the throwing function asynchronously std::future fut = std::async(std::launch::async, throw_exception); try { // get() will rethrow the exception captured from the thread fut.get(); } catch (const std::exception& e) { std::cout << "Caught exception: " << e.what() << std::endl; } return 0; } ``` ### Output ```text Caught exception: Exception thrown from background task ``` --- ## Key Considerations & Best Practices 1. **Single-use `get()`**: The `std::future::get()` method can only be called once. After calling `get()`, the future's shared state is invalidated. If multiple threads need to access the result, use `std::shared_future` instead. 2. **Blocking Behavior**: Calling `get()` or `wait()` blocks the calling thread until the asynchronous task completes. To avoid blocking, you can use `wait_for()` or `wait_until()` to check if the result is ready with a timeout. 3. **Destructor Blocking**: Be aware that a `std::future` returned by `std::async` with `std::launch::async` policy will block in its destructor until the asynchronous task completes. If you do not store the returned future (creating a temporary object), the call to `std::async` becomes synchronous because the temporary future is destroyed immediately at the end of the statement. 4. **Thread Safety**: While the shared state between `std::promise` and `std::future` is thread-safe, the `std::future` object itself is not thread-safe for concurrent access. Use `std::shared_future` if multiple threads need to query the same result.
← Cpp Libs Type_TraitsCpp Libs Mutex β†’