C + + - multithreading (async, future, packaged_task, promise)

Posted by geowulf on Tue, 01 Mar 2022 23:46:22 +0100

Introduction background

If you want to receive the return value of sub threads in the main thread, or realize the information transfer between sub threads, you may use the future object at this time. Using this object requires the introduction of relevant header files.

#include <future>

We will use the future object to receive the return result of the child thread, and use its member function get() to obtain the specific value.

async,future

std::async

std::async is a function template used to start an asynchronous task (automatically create a thread and start executing the corresponding thread entry function). After starting an asynchronous task, it returns an std::future object.

std::async(Child thread entry, Child thread entry parameters); 
  • Pass in related parameters
  1. std::lunch::deferred

It indicates that the call of the thread entry function will be delayed until the wait() or get() function of std::future is called (called by the main thread). In fact, no new threads were created at all. std::lunch::deferred, delayed call, and no new thread is created. It is called thread entrance function in main thread.
If wait() or get() is not called, it will not be executed.

std::async(std::lunch::deferred,Child thread entry, Child thread entry parameters); 
  1. std::launch::async

Create a new thread when calling async function, and this parameter will be passed in by default.

std::future

std::future provides a mechanism to access the results of asynchronous operations, which means that you may not be able to get the results immediately, but you will be able to get the results when the thread is executed in the near future.

  • get()

The get() member function of std::future object will wait for the thread to finish executing and return the result. If it can't get the result, it will wait all the time. It feels a bit like join(). However, it can get the result. The get function has and can only be called once.

  • wait()

It is used to wait for the thread to return. It does not return the result itself. This effect is more similar to the join() of std::thread.

Small example

#include <iostream>
#include <thread>
#include <future>
using namespace std;
int mythread(int num) {
	cout << "mythread Thread on,id:" << this_thread::get_id() << endl;
	//Simulate some operations for 5 seconds
	chrono::milliseconds dura(5000);
	this_thread::sleep_for(dura);
	cout << "mythread Thread end,id:" << this_thread::get_id() << endl;
	return num;
}
int main() {
	cout << "Main thread on,id:" << this_thread::get_id() << endl;
	future<int> res = async(mythread, 25); //Create a thread and execute
	cout << "continue......" << endl;
	int a = res.get(); //Receive the return value of the thread. If the sub thread has not ended, the main thread will be stuck here and wait for the return value of the sub thread
	cout << "Return value of receiving sub thread:" << a << endl;
	cout << "End of main thread,id:" << this_thread::get_id() << endl;
	return 1;
}

std::packaged_task

Class template, whose template parameters are various callable objects through packaged_task wraps all kinds of callable objects so that they can be called as thread entry functions in the future.

std::packaged_task<return type(Parameter type)> mypt(Child thread entry);
int main() {
	cout << "Main thread on,id:" << this_thread::get_id() << endl;
	packaged_task<int(int)> mypt(mythread); //   < return type (parameter type) >
	thread th(ref(mypt), 100); //Create child thread
	th.join();
	future<int> res = mypt.get_future(); // Using packaged objects to obtain future objects
	cout << "Return value of receiving sub thread:" << res.get() << endl;
	cout << "End of main thread,id:" << this_thread::get_id() << endl;
	return 1;
}

  • Callable objects can be replaced by lambda expressions by functions
int main() {
	cout << "Main thread on,id:" << this_thread::get_id() << endl;
	packaged_task<int(int)> mypt([](int num) {
		cout << "mythread Thread on,id:" << this_thread::get_id() << endl;
		//Simulate some operations for 5 seconds
		chrono::milliseconds dura(5000);
		this_thread::sleep_for(dura);
		cout << "mythread Thread end,id:" << this_thread::get_id() << endl;
		return num;
	}); 
	thread th(ref(mypt), 100);
	th.join();
	future<int> res = mypt.get_future();
	cout << "Return value of receiving sub thread:" << res.get() << endl;
	cout << "End of main thread,id:" << this_thread::get_id() << endl;
	return 1;
}

std::promise

We can assign a value to it in a thread, and then we can take this value out in other threads to realize communication.
Usage: mainly pass the std::promise object as a parameter to the child thread.

#include <iostream>
#include <thread>
#include <future>
using namespace std;
void mythread(promise<int> &a,int num) {
	cout << "mythread Thread on,id:" << this_thread::get_id() << endl;
	//Simulate some operations for 5 seconds
	chrono::milliseconds dura(5000);
	this_thread::sleep_for(dura);
	cout << "mythread Thread end,id:" << this_thread::get_id() << endl;
	a.set_value(num);
}
int main() {
	cout << "Main thread on,id:" << this_thread::get_id() << endl;
	promise<int> pro; 
	thread th(mythread,ref(pro), 100);
	th.join();
	future<int> res =pro.get_future();
	cout << "Return value of receiving sub thread:" << res.get() << endl;
	cout << "End of main thread,id:" << this_thread::get_id() << endl;
	return 1;
}

Save a value through promise. At some point in the future, we can get the bound value by binding a future to this promise.

Topics: C++