std::future
- introduce
-
- member function
- effect
- scenes to be used
-
- asynchronous task
- concurrency control
- result acquisition
- usage example
-
- Associate asynchronous tasks with std::async
- Using std::promise with std::future
- Result acquisition and exception handling
- Precautions
- other
-
- std::shared_future
- std::future_status
Introduction
std::future
f
std::future
is a template class in the C++11 Standard Library (Concurrency Support Library) that represents the result of an asynchronous operation. When we use asynchronous tasks in multi-threaded programming, std::future
can help us get the execution results of tasks when needed. An important feature of std::future
is the ability to block the current thread until the asynchronous operation completes, thus ensuring that we do not encounter outstanding operations while fetching results.
Member function
- Constructor
future() noexcept
: Default constructor. Constructs astd::future
with no shared state. After construction,valid()
== falsefuture( future & amp; & amp; other ) noexcept
: Move constructor. Constructs a std::future with move semantics from the shared state of other. other.valid()
== false after construction.future( const future & amp; other ) = delete
: CopyConstructible is not possible.
- Destructor
~future()
; - operator=
future & amp; operator=( future & amp; & amp; other ) noexcept
: releases any shared state and moves the contents of the assignment other to *this . After assignment, other.valid() == false and this->valid() will yield the same value as other.valid() before assignment.future & amp; operator=( const future & amp; other ) = delete
: CopyAssignable is not possible.
share() noexcept
: Transfers the shared state of *this to thestd::shared_future
object. Multiplestd::shared_future
objects can refer to the same shared object, after callingvalid()
== false.T get()
: The get method waits until the future has a valid result and (depending on which template is used) fetches it. It equivalently callswait()
to wait for the result. The generic template and the two template specializations each have a single get version. The three versions of get differ only in the return type. The behavior is undefined ifvalid()
is false before calling this function. Release any shared state.valid()
is false after calling this method.bool valid()
: Check whether the future can share state.wait()
: wait for results to become available- wait_for
- wait_until
Function
- Getting the result of an asynchronous operation:
std::future
provides a mechanism that allows us to safely get the result of an asynchronous operation in a multi-threaded environment. - Hide the details of asynchronous operations:
std::future
encapsulates the results of asynchronous operations, so that programmers do not need to pay attention to the specific implementation details of thread synchronization and communication. - Thread Synchronization: By blocking and waiting for an asynchronous operation to complete,
std::future
can ensure that we have obtained the desired result before continuing to perform other operations. - Exception handling:
std::future
can catch exceptions thrown in asynchronous operations and rethrow them when getting results, or handle them in the main thread , which makes exception handling easier. - Improve performance:
std::future
allows us to better utilize the performance of multi-core processors and improve the execution efficiency of programs by executing tasks in parallel.
Usage scenario
Asynchronous task
When we need to perform some time-consuming operations in the background, such as file read and write, network requests or computationally intensive tasks, std::future
can be used to represent the results of these asynchronous tasks. By separating tasks from the main thread, we can achieve parallel processing of tasks, thereby improving the execution efficiency of the program.
Concurrency control
In multi-threaded programming, we may need to wait for some tasks to complete before proceeding with other operations. By using std::future
, we can achieve synchronization between threads to ensure that the task is completed before getting the result and continuing to perform subsequent operations.
Get the results
std::future
provides a safe way to get the result of an asynchronous task. We can use the std::future::get()
function to get the result of the task. This function blocks the current thread until the asynchronous operation completes. In this way, when calling the get()
function, we can ensure that the desired result has been obtained. If an exception occurs in an asynchronous operation, the get()
function will rethrow the exception, allowing us to handle these exceptions.
Usage example
header file:
#include <future>
Using std::async to associate asynchronous tasks
std::async
is a simple way to associate a task with a std::future
. It creates and runs an asynchronous task and returns a std::future
object associated with the task result.
#include#include <future> #include int long_running_task() { std::this_thread::sleep_for(std::chrono::seconds(3)); return 42; } int main() { std::future result_future = std::async(std::launch::async, long_running_task); // perform other operations here int result = result_future. get(); std::cout << "Result: " << result << std::endl; return 0; }
Using std::promise with std::future
std::promise
is another way to work with std::future
. We can use the std::promise
object to explicitly set the result of the task, and the std::future
object is used to get the result.
#include#include <future> #include void long_running_task(std::promise result_promise) { // Execute the long running task int result = 42; // Set the result to the promise object result_promise.set_value(result); } int main() { std::promise result_promise; std::future result_future = result_promise. get_future(); // create a new thread to execute the long running task std::thread task_thread(long_running_task, std::move(result_promise)); // perform other operations here int result = result_future. get(); std::cout << "Result: " << result << std::endl; task_thread. join(); return 0; }
Result acquisition and exception handling
Use the std::future::get()
function to get the result of an asynchronous task. This function blocks the current thread until the asynchronous operation completes. If an exception occurs during an asynchronous operation, the get()
function will rethrow the exception.
// Note: The std::future::get() function can only be called once. After calling get(), the std::future object becomes invalid. // If you need to access the result multiple times, consider using std::shared_future. try {<!-- --> int result = result_future. get(); std::cout << "Result: " << result << std::endl; } catch (const std::exception & e) {<!-- --> std::cerr << "Exception caught: " << e.what() << std::endl; } catch (...) {<!-- --> std::cerr << "Unknown exception caught" << std::endl; } std::chrono::milliseconds timeout(100); std::future_status status = result_future.wait_for(timeout); if (status == std::future_status::ready) {<!-- --> int result = result_future. get(); std::cout << "Result: " << result << std::endl; } else {<!-- --> std::cerr << "Timeout: the task is still running" << std::endl; }
In order to avoid blocking, we can use the std::future::wait_for()
or std::future::wait_until()
function to wait for a period of time or until a certain moment. These functions return a std::future_status
enumeration value indicating the status of the asynchronous operation (std::future_status::ready
, std::future_status:: timeout
or std::future_status::deferred
).
Notes
When using std::future
, you need to pay attention to the following points:
- The
std::future::get()
function can only be called once. After calling get(), thestd::future
object becomes invalid. If you need to access the result multiple times, consider using std::shared_future. std::future
objects cannot be copied, but can be transferred through the move constructor or move assignment operator. This means we cannot store std::future objects in containers unless wrapped with std::shared_future or pointers.- When using
std::async
, tasks may be executed in the current thread context by default. It depends on library implementation and system resources. To ensure that tasks are executed in a new thread, thestd::launch::async
flag can be used:
std::future result_future = std::async(std::launch::async, long_running_task);
- If the std::future object is still associated with an active asynchronous operation when it is destructed, and the operation has not yet completed, the destructor will block waiting for the operation to complete. In some cases, this can cause the program to deadlock. To avoid this problem, you can explicitly call the wait(), wait_for(), or wait_until() functions before destructing the std::future object.
- Although
std::future
is very useful for obtaining asynchronous task results and thread synchronization, it cannot solve all concurrency problems. For example,std::future
cannot be used to implement complex concurrency patterns such as thread pools, work stealing, etc. For these advanced concurrency requirements, additional libraries or custom implementations may be required. - When using
std::promise
andstd::future
, you need to ensure that the corresponding value has been set before callingget()
, otherwise it will results in undefined behavior. This may require careful design and debugging of the code to ensure the correct order of execution. - The lifetime of a
std::future
object needs to be carefully managed. If astd::future
object is destroyed prematurely, the associated asynchronous task may become inaccessible. Therefore, it is necessary to ensure that the validity of thestd::future
object is maintained until the asynchronous task is completed. - In some cases, using
std::future
may cause performance degradation. For example, when multiple threads are frequently waiting for the result of an asynchronous operation, this can result in thread blocking and context switching overhead. In order to avoid this problem, you can consider using a non-blocking way to query the status of asynchronous operations, such asstd::future::wait_for()
andstd::future::wait_until()
function. In this way, we can perform other tasks while the asynchronous operation is not completed, improving the responsiveness and concurrency performance of the program. std::future
is not suitable for all scenarios. For example, it is not suitable for tasks that need to process multiple inputs or output results, or scenarios that need to implement dynamic task dependencies. In these cases, it may be necessary to look for other concurrency and synchronization solutions.- Although
std::future
provides an exception handling mechanism, it should be noted that once thestd::future::get()
function rethrows an exception, the exception requires Capture and process in the thread that callsget()
. This means that appropriate exception handling strategies need to be set in the main thread and other threads to ensure the stability and robustness of the program.
Other
std::shared_future
std::shared_future
is a variant of std::future
that allows multiple threads to share the results of the same asynchronous operation. Unlike std::future
, std::shared_future
objects can be copied, so they can be stored in containers or passed between multiple threads. Additionally, the std::shared_future::get()
function can be called multiple times without making the std::shared_future
object invalid.
#include#include <future> #include void print_result(std::shared_future result_future) { int result = result_future. get(); std::cout << "Result: " << result << std::endl; } int main() { std::promise result_promise; std::shared_future result_future = result_promise. get_future(). share(); std::thread t1(print_result, result_future); std::thread t2(print_result, result_future); result_promise. set_value(42); t1. join(); t2. join(); return 0; }
std::future_status
std::future_status
is an enumeration type, indicating the status of asynchronous operations. It has three possible values:
std::future_status::ready
: The asynchronous operation has completed and the result is available.std::future_status::timeout
: The asynchronous operation has not yet completed, and the wait has timed out.std::future_status::deferred
: the asynchronous operation has been deferred and has not yet started (only if thestd::launch::deferred
strategy was used to create astd::future
object).
We can use the std::future::wait_for()
and std::future::wait_until()
functions to query the status of an asynchronous operation without blocking the current thread.
#include#include <future> #include int long_running_task() { std::this_thread::sleep_for(std::chrono::seconds(3)); return 42; } int main() { std::future result_future = std::async(std::launch::async, long_running_task); std::chrono::milliseconds timeout(1000); std::future_status status = result_future.wait_for(timeout); if (status == std::future_status::ready) { int result = result_future. get(); std::cout << "Result: " << result << std::endl; } else { std::cerr << "Timeout: the task is still running" << std::endl; } // wait for the task to complete so that the result can be retrieved correctly int result = result_future. get(); std::cout << "Final result: " << result << std::endl; return 0; }
In the above example, we used the std::future::wait_for()
function to query the status of the asynchronous operation. If the status is std::future_status::ready
, we can get the result immediately. Otherwise, we can perform other tasks during the waiting period to improve the responsiveness and concurrency performance of the program. Finally, before exiting the main
function, we call result_future.get()
to ensure that the result of the asynchronous operation is correctly retrieved.