(10) Future other member functions, shared_future,atomic

Keywords: C++ Concurrent Programming

Section 9. async, future, packaged_task,promise

This section needs to include header files #include < future >

1, std::async and std::future create background tasks and return values
std::async is a function template used to start an asynchronous task. After starting an asynchronous task, it returns an std::future object, which is a class template.

What is "start an asynchronous task"? It is to automatically create a thread and start executing the corresponding thread entry function. It returns an std::future object, which contains the results returned by the thread entry function. We can obtain the results by calling the member function get() of the future object.

"Future" means the future. Some people also call it std::future. It provides a mechanism to access the results of asynchronous operations, that is, 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. Therefore, it is understood that there is a value stored in future, This value can be obtained at some time in the future.

The get() member function of the 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 results.

The wait() member function of std::future object is used to wait for the thread to return. It does not return results. This effect is more similar to the join() of std::thread.

#include <iostream>
#include <future>
using namespace std;
class A {
public:
	int mythread(int mypar) {
		cout << mypar << endl;
		return mypar;
	}
};
int mythread() {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
} 
int main() {
	A a;
	int tmp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result1 = std::async(mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; //The card waits here for mythread() to finish executing and get the result
	//Class member function
	std::future<int> result2 = std::async(&A::mythread, &a, tmp); //The second parameter is the object reference to ensure that the same object is executed in the thread
	cout << result2.get() << endl;
   //Or result2.wait();
	cout << "good luck" << endl;
	return 0;
}

We achieve some special purposes by passing a parameter to std::async(), which is of type std::launch (enumeration type):

1,std::lunch::deferred:
(defer, delay) means 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); If wait () or get () is not called, it will not be executed.
In fact, no new threads were created at all. std::launch::deferred, delayed call, and no new thread is created. It is called thread entrance function in main thread.

#include <iostream>
#include <future>
using namespace std;
int mythread() {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}
int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result1 = std::async(std::launch::deferred ,mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; //The card waits here for mythread() to finish executing and get the result
	cout << "good luck" << endl;
	return 0;
}

Always print out continue... Before printing out information such as mythread() start and mythread() end.

2. std::launch::async, which starts to create a new thread when calling the async function.

int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result1 = std::async(std::launch::async ,mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; 
	cout << "good luck" << endl;
	return 0;
}

2, std::packaged_task: package the task and wrap the task.
Class template, whose template parameters are various callable objects through packaged_task wraps various callable objects, which can be called as thread entry functions in the future.

#include <thread>
#include <iostream>
#include <future>
using namespace std;
int mythread(int mypar) {
	cout << mypar << endl;
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}
int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	//We pass the function mythread through packaged_ Wrap the task
    //The parameter is an int and the return value type is int
    std::packaged_task<int(int)> mypt(mythread);
	std::thread t1(std::ref(mypt), 1);
	t1.join();
	std::future<int> result = mypt.get_future(); 
	//The std::future object contains the returned results of the thread entry function. Here, result saves the results returned by mythread.
	cout << result.get() << endl;
	return 0;
}

Callable objects can be replaced by lambda expressions by functions

int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::packaged_task<int(int)> mypt([](int mypar) {
		cout << mypar << endl;
		cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
		std::chrono::milliseconds dura(5000);
		std::this_thread::sleep_for(dura);
		cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
		return 5;
	}); 
	std::thread t1(std::ref(mypt), 1);
	t1.join();
	std::future<int> result = mypt.get_future(); 
	//The std::future object contains the returned results of the thread entry function. Here, result saves the results returned by mythread.
	cout << result.get() << endl;
}

packaged_ The callable object wrapped by task can also be called directly. From this point of view, packaged_ The task object is also a callable object
Direct call of lambda

int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::packaged_task<int(int)> mypt([](int mypar) {
		cout << mypar << endl;
		cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
		std::chrono::milliseconds dura(5000);
		std::this_thread::sleep_for(dura);
		cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
		return 5;
	}); 
	mypt(1);
	std::future<int> result = mypt.get_future();
	cout << result.get() << endl;
}

std::promise, class template
We can assign a value to it in one thread, and then we can take this value out in other threads

#include <thread>
#include <iostream>
#include <future>
using namespace std;
void mythread(std::promise<int> &tmp, int clac) {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	int result = clac;
	tmp.set_value(result); //The results are saved to the tmp object
	return;
}
vector<std::packaged_task<int(int)>> task_vec;
int main() {
	std::promise<int> myprom;
	std::thread t1(mythread, std::ref(myprom), 180);
	t1.join(); //Here the thread has finished executing
	std::future<int> fu1 = myprom.get_future(); //promise and future binding, used to get the return value of the thread
	auto result = fu1.get();
	cout << "result = " << result << endl;
}

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

Note: when using thread, you must join() or detach(), otherwise the program will report an exception

Summary:

The purpose of learning these things is not to use them in practical development.

On the contrary, if we can write a stable and efficient multithreaded program with the least things, it is more commendable.

In order to grow, we must read the code written by some experts, so as to realize the accumulation of our own code;

Posted by jimdidr on Sat, 16 Oct 2021 21:54:53 -0700