C++’s std:promise
is a powerful tool for managing asynchronous data transfers between threads. It is defined in the <future>
header and works in tandem with std::future
to synchronize thread operations. Let’s dive into its mechanics, usage, and some practical examples.
std::promise
formsThe std::promise
class template is available in three forms:
Base template: It’s the generic form used for most data types. It can be written as template< class R > class promise;
.
Nonvoid specialization: It’s used when passing nonvoid types (like objects) between threads. It can be written as template< class R > class promise<R&>;
.
Void specialization: It signals state changes or events without passing data. It can be written as template<> class promise<void>;
.
A std::promise
object is tied to a shared state, representing either a value, an exception, or a state of readiness. Here are the key operations a std::promise
can perform the following:
Make ready: It stores a result or exception in the shared state and unblocks any waiting threads.
Release: It releases the reference to the shared state, potentially destroying it if it’s the last reference.
Abandon: It stores a std::future_error
exception and readies the state before releasing it.
Now that we have a foundational understanding of std::promise
and its various forms, let’s delve into some practical applications. These code examples will illustrate how std::promise
can be effectively utilized in different scenarios, providing a clearer perspective on its functionality and versatility.
This example demonstrates how to transfer a simple integer value from a secondary thread back to the main thread using std::promise
and std::future
. The secondary thread performs some work and sets the promise’s value, which is then retrieved by the main thread through the future. This pattern is crucial for tasks where the main thread needs to wait for a result calculated by a secondary thread.
#include <iostream>#include <future>#include <thread>// This function will be executed in a separate threadvoid threadFunction(std::promise<int>&& promiseObj) {// Simulate some work in the thread// ...// Set the result of the promise to 42promiseObj.set_value(42);}int main() {// Create a std::promise object that can hold an integerstd::promise<int> promise;// Obtain a std::future object from the promise// The future object will eventually hold the result that the promise setsstd::future<int> future = promise.get_future();// Start a new thread and pass the promise to it using std::move// std::move is used because promises cannot be copied, only movedstd::thread th(threadFunction, std::move(promise));// Wait for the result from the future object// This will block until the thread sets a value in the promiseint result = future.get();// Output the result obtained from the threadstd::cout << "Result from Thread: " << result << std::endl;// Wait for the thread to complete its execution// This is necessary to ensure that the program does not exit before the thread finishesth.join();return 0;}
In this example, we use std::promise
to pass an integer from a secondary thread back to the main thread. The secondary thread sets the value of the promise, which the main thread retrieves using a future.
This example uses std::promise<void>
to signal the completion of a task in a secondary thread without transferring any specific data. It illustrates how to use the void specialization of std::promise
to synchronize operations between threads, especially useful in scenarios where the main thread needs to wait for a signal from secondary threads indicating that their tasks are complete.
#include <iostream>#include <future>#include <thread>// Function to be executed in the secondary threadvoid threadFunction(std::promise<void>&& promiseObj) {// Perform some actions in the thread// ...// Signal the completion of the taskpromiseObj.set_value();}int main() {// Create a std::promise object that doesn't hold any specific datastd::promise<void> promise;// Obtain a std::future object from the promise// This future will be used to wait for a signal from the secondary threadstd::future<void> future = promise.get_future();// Start a new thread, passing the promise to it// The std::move is necessary because the promise cannot be copied, only movedstd::thread th(threadFunction, std::move(promise));// Wait for the secondary thread to signal completion// The main thread will block here until the promise is fulfilledfuture.wait();// After the signal is received, print a confirmation messagestd::cout << "Thread completed its action" << std::endl;// Wait for the secondary thread to finish its execution// This ensures the program doesn't exit prematurelyth.join();return 0;}
Here, std::promise<void>
is used to signal the completion of an action in a thread without transferring any data.
In this final example, a secondary thread throws an exception that is caught and forwarded to the main thread through the promise-future mechanism. This demonstrates how std::promise
can be used to propagate exceptions from a secondary thread to the main thread, enabling robust error handling in multithreaded applications.
#include <iostream>#include <future>#include <thread>#include <exception>// Function to be executed in a separate threadvoid threadFunction(std::promise<int>&& promiseObj) {try {// Simulating some work in the thread that might throw an exceptionthrow std::runtime_error("Exception from thread");} catch(...) {// If an exception is caught, pass it to the main thread// std::current_exception captures the current exception in a std::exception_ptrpromiseObj.set_exception(std::current_exception());}}int main() {// Create a std::promise object that will hold an integerstd::promise<int> promise;// Get a std::future object from the promise// This future will be used to retrieve the result or exception set by the secondary threadstd::future<int> future = promise.get_future();// Start a new thread, passing the promise to itstd::thread th(threadFunction, std::move(promise));try {// Attempt to get the result from the future// This call will block until the secondary thread sets a value or an exceptionint result = future.get();// (Optional) Process the result here} catch(const std::exception& e) {// If an exception was thrown in the secondary thread, catch it herestd::cout << "Main thread caught exception: " << e.what() << std::endl;}// Ensure that the thread completes its executionth.join();return 0;}
In this final example, the thread throws an exception, which is caught and passed back to the main thread through the promise-future mechanism.
std::promise
in C++ offers a robust way to manage asynchronous operations between threads, either by transferring data, signaling events, or propagating exceptions. Understanding and using this tool effectively can greatly enhance the robustness and efficiency of multithreaded applications in C++.
Free Resources