Threads
Contents
Async
Sometimes we need to execute a function but we do not want to wait for the function to return. Instead we want to proceed with the code and obtain the result of the function at a later time. This can be done with the "std::async" call.File: async1.cpp
#include <iostream> #include <future> #include <chrono> #include <condition_variable> #include <iostream> #include <mutex> #include <string> #include <thread> using namespace std ; int calculate_value() { cout << "Calculating value in background...\n"; this_thread::sleep_for(chrono::seconds(20)); // Simulate long computation return 42; } int main() { future<int> future_result = async(launch::async, calculate_value); cout << "Main thread continues to execute...\n"; cout << "Waiting for result...\n"; int result = future_result.get(); // Blocks until the result is available cout << "Result is: " << result << endl; return 0; } $ ./a.exe Main thread continues to execute... Calculating value in background... Waiting for result... Result is: 42 We use the call: futurefuture_result = async(launch::async, calculate_value); to start the worker thread. This returns a future object. We specify the type that the function "calculate_value" returns in the "future" class. The "async" call returns immediately. A new thread is created and the function "calculate_value" starts executing in the new thread. We then use the "get()" call on the future object. int result = future_result.get(); This call will block till the "calculate_value" finishes. Once the result is obtained, the worker thread exits and the main thread prints the result. We now study the option "launch::deferred" instead of "launch::async" . With deferred we don't call the function in a separate thread but call it in the same thread when "get" or "wait" is called.
File: async1a.cpp
#include <iostream> #include <future> #include <chrono> #include <condition_variable> #include <iostream> #include <mutex> #include <string> #include <thread> using namespace std ; int calculate_value() { cout << "Calculating value in background...\n"; this_thread::sleep_for(chrono::seconds(20)); // Simulate long computation return 42; } int main() { future<int> future_result = async(launch::deferred, calculate_value); cout << "Main thread continues to execute. Waits for 10 seconds\n"; this_thread::sleep_for(chrono::seconds(10)); cout << "Waiting for result...\n"; int result = future_result.get(); // Blocks until the result is available cout << "Result is: " << result << endl; return 0; } $ g++ async1a.cpp ; ./a.exe Main thread continues to execute. Waits for 10 seconds Waiting for result... Calculating value in background... Result is: 42 The "async" call in the above program uses the option "launch::deferred" instead of "launch::async"We have the "wait" call that is similar to the "get" call but does not return a result.
File: async1b.cpp
#include <iostream> #include <future> #include <chrono> #include <condition_variable> #include <iostream> #include <mutex> #include <string> #include <thread> using namespace std ; int calculate_value() { cout << "Calculating value in background...\n"; this_thread::sleep_for(chrono::seconds(20)); // Simulate long computation return 42; } int main() { future<int> future_result = async(launch::async, calculate_value); cout << "Main thread continues to execute. Waits for 10 seconds\n"; this_thread::sleep_for(chrono::seconds(10)); cout << "Waiting for result...\n"; future_result.wait(); // Blocks until the result is available cout << "function call completed. " << endl; return 0; } $ g++ async1b.cpp ; ./a.exe Main thread continues to execute. Waits for 10 seconds Calculating value in background... Waiting for result... function call completed.We also have the concept of "promise". This gives us more control over the return value. The async function can set the return value in the middle of the function and it can be retreived in the main thread.
We can also use "wait_for" to check if the async function finished
File: async_wait.cpp
#include <iostream> #include <future> #include <chrono> #include <thread> #include <string> using namespace std ; using namespace chrono_literals; // Enables the use of s, ms, etc. // A function that simulates a long-running task string long_running_task() { // Simulate work this_thread::sleep_for(chrono::seconds(2)); return "Task Completed"; } int main() { cout << "Main thread: Starting asynchronous task..." << endl; // Launch the task asynchronously with a specific policy future<string> future = async(launch::async, long_running_task); cout << "Main thread: Waiting for result with a 1-second timeout check..." << endl; // Check the future's status periodically while (true) { // wait_for returns a status indicating if the result is ready, timed out, or deferred // wait for 1 second future_status status = future.wait_for(1s); if (status == future_status::ready) { cout << "Main thread: Task is ready. Getting result." << endl; // Get the result (this call will not block now) string result = future.get(); cout << "Result: " << result << endl; break; } else if (status == future_status::timeout) { // This happens every 1 second in the loop cout << "Main thread: Timeout reached, but task is still running... checking again." << endl; } // future_status::deferred is also possible if launch::async was not used } cout << "Main thread: Program finished." << endl; return 0; } $ g++ async_wait.cpp ; ./a.exe Main thread: Starting asynchronous task... Main thread: Waiting for result with a 1-second timeout check... Main thread: Timeout reached, but task is still running... checking again. Main thread: Task is ready. Getting result. Result: Task Completed Main thread: Program finished.
File: promise1.cpp
#include <iostream> #include <thread> #include <future> #include <chrono> using namespace std ; // Function executed in a separate thread void calculate_sum(int a1, int b1, promise<int>& promise_obj) { cout << "Worker thread: Calculating sum..." << endl; // Simulate some long work this_thread::sleep_for(chrono::seconds(2)); int sum = a1 + b1 ; // Fulfill the promise by setting the value promise_obj.set_value(sum); cout << "Worker thread: Value set." << endl ; this_thread::sleep_for( chrono::seconds(20) ) ; cout << "Exiting calculate_sum" << endl ; } int main() { // 1. Create a promise object for an integer result promise<int> promise_obj; // 2. Get the associated future object from the promise future<int> future_obj = promise_obj.get_future() ; // 3. Launch a worker thread, passing the promise object by reference thread worker_thread(calculate_sum, 5, 10, ref(promise_obj)) ; // 4. In the main thread, wait for the result using the future cout << "Main thread: Waiting for the result..." << endl; // get() blocks the current thread until the promise has a value //However int result = future_obj.get(); cout << "Main thread: Retrieved result is: " << result << endl; // Join the worker thread to the main thread worker_thread.join(); cout << "End of main: " << endl; return 0; }
Exercise
1) Discuss the 2 approaches in the below program.File: async_ex1.cpp
#include <iostream> #include <future> #include <chrono> #include <condition_variable> #include <iostream> #include <mutex> #include <string> #include <thread> using namespace std ; int globalResult = 0 ; int calculate_value() { cout << "Calculating value in background...\n"; this_thread::sleep_for(chrono::seconds(20)); // Simulate long computation globalResult = 42 ; return 42; } int main() { future<int> future_result = async(launch::async, calculate_value); cout << "Main thread continues to execute...\n"; cout << "Waiting for result...\n"; int result = future_result.get(); // Blocks until the result is available cout << "Result is: " << result << endl; thread th1( calculate_value ) ; th1.join() ; cout << "Result is: " << globalResult << endl; return 0; }