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.
YouTip