Hi, friends! I’m Hui Mei! 😊 Today we are going to talk about a very useful tool in C++ — **std::async
and std::future
**, which make asynchronous programming simple and efficient. If you’ve ever struggled with issues like locks and callback functions in multithreaded programming, today’s content will surely open up a new world for you!
With the help of the C++ standard library’s std::async
and std::future
, you can easily launch asynchronous tasks and retrieve the results when needed. This approach not only makes your code cleaner but also helps avoid common multithreading pitfalls, such as resource contention and deadlocks.
Today we will learn from the following aspects:
-
What is asynchronous programming? The basic concepts of std::async
andstd::future
. -
How to use them to launch asynchronous tasks and obtain results? -
Practical application scenarios and code examples. -
Common questions and precautions.
Are you ready? Let’s get started! 🚀
1. What is Asynchronous Programming?
1. Asynchronous Programming vs Synchronous Programming
In traditional synchronous programming, the program waits for a task to complete before continuing execution. For example, suppose you are doing two things: boiling water and chopping vegetables. If executed synchronously, you must wait for the water to boil before you can start chopping.
In asynchronous programming, the program can execute multiple tasks simultaneously. For example, you can let the boiling water task run in the background while you continue chopping vegetables in the foreground. This not only increases efficiency but also makes full use of system resources.
In C++, std::async
is a key tool for implementing asynchronous programming. It can start an asynchronous task and return a std::future
object for retrieving the task’s execution result.
2. Basic Concepts of std::async
and std::future
-
**
std::async
**: -
A function template used to launch asynchronous tasks. -
Asynchronous tasks can run in independent threads or can be executed later in the calling thread. -
**
std::future
**: -
Used to store the results of asynchronous tasks. -
The result can be obtained through the get()
method. If the task is not completed,get()
will block the calling thread until the result is available.
2. How to Use std::async
and std::future
?
1. Basic Usage: Launching Asynchronous Tasks and Obtaining Results
Here’s a simple example demonstrating how to use std::async
to launch an asynchronous task and obtain its result through std::future
:
#include <iostream>
#include <future> // Include std::async and std::future
// A simple function to simulate a time-consuming task
int computeSquare(int x) {
std::cout << "Calculating..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2)); // Simulate a time-consuming operation
return x * x;
}
int main() {
// Use std::async to launch an asynchronous task
std::future<int> result = std::async(computeSquare, 5);
std::cout << "Asynchronous task started, main thread can continue with other operations!" << std::endl;
// Call get() to obtain the result of the asynchronous task
int square = result.get();
std::cout << "Calculation result:" << square << std::endl; // Output: 25
return 0;
}
Output:
The asynchronous task has started, the main thread can continue with other operations!
Calculating...
Calculation result: 25
2. Code Analysis
-
std::async
starts the asynchronous task:
-
std::async(computeSquare, 5)
starts an asynchronous task that callscomputeSquare(5)
. -
The return value is a std::future<int>
that stores the task’s return result.
Obtaining the Result of the Asynchronous Task:
-
Calling result.get()
will block the current thread until the asynchronous task completes and returns a result. -
get()
can only be called once, after which it will release the task’s result.
Main Thread Continues Execution:
-
While the asynchronous task is running, the main thread can continue executing other operations, only calling get()
when the result is needed.
3. Controlling Task Execution Strategies
std::async
supports two task execution strategies:
-
** std::launch::async
**: Forces the task to execute asynchronously in a new thread. -
** std::launch::deferred
**: The task is executed only whenget()
orwait()
is called.
Example: Specifying Execution Strategy
#include <iostream>
#include <future>
#include <thread>
int compute(int x) {
std::this_thread::sleep_for(std::chrono::seconds(1));
return x * 2;
}
int main() {
// Force asynchronous execution
std::future<int> asyncResult = std::async(std::launch::async, compute, 10);
// Deferred execution
std::future<int> deferredResult = std::async(std::launch::deferred, compute, 20);
std::cout << "Main thread continues to execute..." << std::endl;
// Get asynchronous task results
std::cout << "Asynchronous result:" << asyncResult.get() << std::endl; // Output: 20
std::cout << "Deferred result:" << deferredResult.get() << std::endl; // Output: 40
return 0;
}
Output:
Main thread continues to execute...
Asynchronous result: 20
Deferred result: 40
3. Practical Application Scenarios
1. Parallel Computing Tasks
When multiple time-consuming tasks need to be run, std::async
can be used to allocate them to different threads for execution, greatly enhancing program performance.
Example: Parallel Computing Multiple Tasks
#include <iostream>
#include <future>
int task1() {
std::this_thread::sleep_for(std::chrono::seconds(2));
return 10;
}
int task2() {
std::this_thread::sleep_for(std::chrono::seconds(3));
return 20;
}
int main() {
// Launch two asynchronous tasks
std::future<int> result1 = std::async(task1);
std::future<int> result2 = std::async(task2);
std::cout << "Waiting for tasks to complete..." << std::endl;
// Get task results
int sum = result1.get() + result2.get();
std::cout << "Total result:" << sum << std::endl; // Output: 30
return 0;
}
Output:
Waiting for tasks to complete...
Total result: 30
2. Asynchronous File Processing
Suppose we need to read the contents of multiple files simultaneously; we can use std::async
to process each file in parallel, enhancing reading efficiency.
4. Tips and Precautions
Tips
-
Avoid Shared Data Contention:
-
If asynchronous tasks need to access shared data, ensure to use std::mutex
or other synchronization mechanisms to avoid data contention.
Checking the Status of std::future
:
-
Before calling get()
, you can check if thefuture
contains a valid result usingvalid()
:if (result.valid()) { int value = result.get(); }
Exception Handling:
-
If an asynchronous task throws an exception, calling get()
will rethrow it, which can be caught usingtry-catch
.
Common Questions
Q1: What is the difference between std::async
and manually creating threads? A1: std::async
is higher level and automatically manages the thread’s lifecycle, whereas manually creating threads requires the developer to manage it themselves.
Q2: Can get()
be called multiple times? A2: No! get()
can only be called once, after which it will release the task’s result. If you need to access the result multiple times, you can store it in a variable.
5. Exercises
-
Use std::async
to start three asynchronous tasks to calculate the sum from 1 to 100, from 101 to 200, and from 201 to 300, and output the total sum. -
Modify the parallel computing example to use the specified std::launch::async
execution strategy. -
Simulate an asynchronous file download task, outputting ‘Download Complete’ after the task finishes.
6. Summary
Today we learned about **std::async
and std::future
** in C++, which are powerful tools for asynchronous programming, enabling easy parallel task processing while ensuring code simplicity and maintainability. Through code examples and exercises, I believe you have mastered their basic usage and practical application scenarios.
Friends, that’s it for today’s C++ learning journey! Remember to practice hands-on, and feel free to ask me any questions in the comments. I wish everyone a happy learning experience and continuous improvement in C++ skills! 🎉