c++11 Multithreaded programming std::async introduction and examples

  • 2020-11-26 18:56:57
  • OfStack

This section discusses how std::async is used in C++11 to perform asynchronous task.

C++11 introduced std::async

What is a std: : async

std::async() is a function template that takes callbacks (functions or function objects) as arguments and may execute them asynchronously.


template<class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&...args);

std::async returns 1 std::future < T > , which stores the value returned by the function object executed by std::async().

The expected arguments of the function can be passed to std::async() as arguments following the function pointer argument.

The first parameter in std::async is the startup policy, which controls the asynchronous behavior of std::async. We can create std::async using three different startup policies

std: : launch: : async

Asynchronous behavior is guaranteed, that is, the transfer function will execute in a separate thread

std: : launch: : deferred

Non-asynchronous behavior is invoked when another thread calls get() to access the Shared state

std: : launch: : async | std: : launch: : deferred

Default behavior. With this startup policy, it can run asynchronously or not, depending on the load on the system, but we have no control over it.

If we do not specify a boot policy, it will behave like std::launch::async | std::launch::deferred

In this section we will use the std::launch::async startup strategy

We can pass any callback on std::async, such as:

· Function pointer

· Function objects

· lambda expression

std: : async demand

Suppose we have to get some data (strings) from the database and the file system, and then we need to merge the strings and print them.

In a single thread, we do this:


#include <iostream>
#include <string>
#include <chrono>
#include <thread>
 
using namespace std::chrono;
 
std::string fetchDataFromDB(std::string recvData) {
 // Make sure that the function is 5 Seconds to complete 
 std::this_thread::sleep_for(seconds(5));
 
 // Handle creating a database connection, fetching data, and so on 
 return "DB_" + recvData;
}
 
std::string fetchDataFromFile(std::string recvData) {
 // Make sure that the function is 5 Seconds to complete 
 std::this_thread::sleep_for(seconds(5));
 
 // Process the fetch file data 
 return "File_" + recvData;
}
 
int main() {
 // Get start time 
 system_clock::time_point start = system_clock::now();
 
 // Get data from the database 
 std::string dbData = fetchDataFromDB("Data");
 
 // Get data from a file 
 std::string fileData = fetchDataFromFile("Data");
 
 // Get end time 
 auto end = system_clock::now();
 
 auto diff = duration_cast<std::chrono::seconds>(end - start).count();
 std::cout << "Total Time taken= " << diff << "Seconds" << std::endl;
 
 // The assembly data 
 std::string data = dbData + " :: " + fileData;
 
 // Output the assembled data 
 std::cout << "Data = " << data << std::endl;
 
 return 0;
}

Output:

[

Total Time Taken = 10 Seconds
Data = DB_Data :: File_Data

]

Since the functions fetchDataFromDB() and fetchDataFromFile() run for 5 seconds each in a separate thread, the total time is 10 seconds.

Since retrieving data from the database and file system is independent and time consuming, we can run them in parallel.

One way is to create a new thread and pass one promise as an argument to the thread function and get data from the associated std::future object in the calling thread

Another way is to use std::async

Use the function pointer to call std::async as a callback

Modify the code above and call fetchDataFromDB() asynchronously using std::async


std::future<std::string>resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
 
std::string dbData = resultDromDB.get()

std::async() does the following

· Automatically create 1 thread (or pick from the internal thread pool) and 1 promise object.

· The std::promise object is then passed to the thread function and the associated std::future object is returned

· When the function we passed the parameter exits, its value will be set in the promise object, so the final return value will be available in the std::future object

Now change the above example and use std::async to asynchronously fetch data from the database


#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <future>
 
using namespace std::chrono;
 
std::string fetchDataFromDB(std::string recvData) {
 // Make sure that the function is 5 Seconds to complete 
 std::this_thread::sleep_for(seconds(5));
 
 // Handle creating a database connection, fetching data, and so on 
 return "DB_" + recvData;
}
 
std::string fetchDataFromFile(std::string recvData) {
 // Make sure that the function is 5 Seconds to complete 
 std::this_thread::sleep_for(seconds(5));
 
 // Process the fetch file data 
 return "File_" + recvData;
}
 
int main() {
 // Get start time 
 system_clock::time_point start = system_clock::now();
 
 std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data");
 
 // Get data from a file 
 std::string fileData = fetchDataFromFile("Data");
 
 // from DB To get the data 
 // The data in future<std::string> Object before it can be retrieved 1 The straight block 
 std::string dbData = resultFromDB.get();
 
 // Get end time 
 auto end = system_clock::now();
 
 auto diff = duration_cast<std::chrono::seconds>(end - start).count();
 std::cout << "Total Time taken= " << diff << "Seconds" << std::endl;
 
 // The assembly data 
 std::string data = dbData + " :: " + fileData;
 
 // Output the assembled data 
 std::cout << "Data = " << data << std::endl;
 
 return 0;
}

Output:

[

Total Time taken= 5Seconds
Data = DB_Data :: File_Data

]

It only took 5 seconds

Call std::async with the Function object as a callback


/*
* Function Object
*/
struct DataFetcher {
 std::string operator ()(std::string recvdData) {
  // Make sure that the function is 5 Seconds to complete 
  std::this_thread::sleep_for(seconds(5));
  // Process the fetch file data 
  return "File_" + recvdData;
 
 }
};
 
// Call with a function object std::async
std::future<std::string> fileResult = std::async(DataFetcher(), "Data"); 

Use the lambda function as a callback to call std::async


std::future<std::string> resultFromDB = std::async([](std::string recvdData) {
 
 std::this_thread::sleep_for(seconds(5));
 // Handle creating a database connection, fetching data, and so on 
 return "DB_" + recvdData;
 
}, "Data"); 

conclusion


Related articles: