同步任务与异步任务:
同步任务:发出一个功能调用时,在没得到结果前,该调用永不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事。个人理解是就像thread的join(),你不执行完该线程就不能继续执行主函数。
异步任务:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。个人理解同thread的detach(),你不用执行完就可以继续进行主程序,只是他还会在执行完后通知调用者。
async 与future
async用于创建异步任务并返回值,future用于获取返回值(通过get()函数获取结果),都在头文件<future>中。
future有两个主要使用的函数,一个是wait(),另一个是get()。wait()可以使进程等待到线程执行完毕(同join()),可以与get()一起使用(虽然没啥必要),可以调用多次。get()函数用于获取线程的返回值,且进程会卡在get函数直到异步任务返回值;而且get函数只能调用一次,因为get()会将future中的值给移动走,如果想不移动走而是复制一个值出来的话要用share_future。
async有两个重载函数,区别是有无launch参数。future是一个类,主要是调用其中的get函数来获取返回值。
用法一:future<返回值类型> 对象名 = async(具有返回值的函数名, 函数参数);
#include<iostream>
#include<future>
#include<windows.h>
using namespace std;
class A {
private:
mutex mymutex;
condition_variable mycond;
int i = 5;
public:
int hanshu() {
cout << "调用函数" << endl;
Sleep(5000); //等待5秒,用来证明进程会在get时等待。
return 4;
}
};
int main() {
A a;
future<int> future1 = async(&A::hanshu, &a);
cout << "111" << endl;
future1.wait();
int t = future1.get();
cout << t << endl;
cout << "222" << endl;
return 0;
}
用法二:future<返回值类型> 对象名 = async(launch型的参数,具有返回值的函数名, 函数参数);
用法一其实就是调用用法二,但launch的参数为(launch::async | launch::deferred)它会在系统资源不够创建一个新线程时创建一个同步任务(在主线程中运行),否则创建一个异步任务。
使用launch::deferred时会直到调用get()或wait()函数时才调用该线程。
使用launch::async时会在创建时调用该线程。
#include<iostream>
#include<future>
#include<windows.h>
using namespace std;
class A {
private:
mutex mymutex;
condition_variable mycond;
int i = 5;
public:
int hanshu() {
cout << "调用函数" << endl;
Sleep(5000); //等待5秒,用来证明进程会在get时等待。
return 4;
}
};
int main() {
A a;
//future<int> future1 = async(launch::async, &A::hanshu, &a);
future<int> future1 = async(launch::deferred, &A::hanshu, &a);
cout << "111" << endl;
future1.wait();
int t = future1.get();
cout << t << endl;
cout << "222" << endl;
return 0;
}
在上面我们说过,future的get函数会把值从里面移走,使得只能get一次,但当我们需要在多个线程调用这个返回值时,就需要使用shared_future了,它因为get是复制,所以可以get多次,创建如下。shared_future<返回值类型> 名称(move(已绑定线程的future类型))或者调用future的share函数,具体情况如下。
#include<iostream>
#include<future>
#include<algorithm>
using namespace std;
int hanshu() {
cout << "调用函数" << endl;
return 3;
}
int main() {
future<int> future1 = async(hanshu);
//shared_future<int> sfuture1(future1.share());
shared_future<int> sfuture1(move(future1));
cout << sfuture1.get() << endl;
cout << sfuture1.get() << endl;
return 0;
}
future的其他成员函数:wait_for、wait_until;这两个函数都需要一个参数作为等待时间,返回值是一个枚举类型future_status,有三种:timeout(超时,即在等待时间内线程未执行完毕)、ready(已完成,即在等待时间内线程已执行完毕,立即返回,不用等到等待时间)、deferred(线程参数选择了deferred,并未开始执行)。
wait_for与wait_until区别:
参数类型不同,wait_for的参数常用格式为:chrono::seconds(i)、chrono::milliseconds(i),即等待i秒或者等待i毫秒;wait_until的参数格式为:chrono::system_clock::time_point,即未来的某个时间点。
#include<iostream>
#include<future>
using namespace std;
int hanshu(int a) {
cout << "线程开始执行" << endl;
return a;
}
int main() {
future<int> future1 = async(hanshu, 5);
//wait_for
future_status stu = future1.wait_for(chrono::milliseconds(5));
if (stu == future_status::deferred) {
cout << "线程未执行" << endl;
}
else if (stu == future_status::ready) {
cout << "等待时间内线程执行完毕" << endl;
}
else{
//stu == future_status::timeout
cout << "等待时间内线程未执行完毕" << endl;
}
//wait_until
std::chrono::system_clock::time_point two_seconds_passed = std::chrono::system_clock::now() + std::chrono::seconds(2); //后面加的时间就是等待时间
future_status stu1 = future1.wait_until(two_seconds_passed);
if (stu1 == future_status::deferred) {
cout << "线程未执行" << endl;
}
else if (stu1 == future_status::ready) {
cout << "等待时间内线程执行完毕" << endl;
}
else {
//stu1 == future_status::timeout
cout << "等待时间内线程未执行完毕" << endl;
}
return 0;
}
packaged_task:
packaged_task在<future>头文件中,是一个类模板,参数是可调用对象(可以是普通函数也可以是lambda函数或函数对象,但类的成员函数好像不行,希望有大佬能告知),可以将各种可调用对象包装起来作为线程入口便于其异步调用。
创建如下:std::packaged_task<函数返回值类型(函数参数类型)> 对象名(可调用对象);
string hanshu(int shu, int v) {
cout << "调用函数, id = " << this_thread::get_id() << endl;
//Sleep(5000);
string t = to_string(shu);
return t;
}
int main() {
std::packaged_task<string(int, int)> pack1(hanshu); //这里string代表函数返回值类型,两个int代表参数类型。
return 0;
}
单纯创建只是将函数封装起来,要调用的话还是需要使用thread。由于packaged_task不可复制,但可以移动,所以在调用时要用move或者ref
string hanshu(int shu, int v) {
cout << "调用函数, id = " << this_thread::get_id() << endl;
//Sleep(5000);
string t = to_string(shu);
return t;
}
int main() {
std::packaged_task<string(int, int)> pack1(hanshu);
//调用包装起来的函数,记得要用ref或move
//thread thread1(ref(pack1), 3, 3);
thread thread1(move(pack1), 3, 3);
thread1.join();
return 0;
}
thread只能让函数运行起来,要获取返回值还是需要future。注意,如果thread使用的是move的话,future要在move前先绑定pack1(即在thread前调用get_future()),shared_future同理.
string hanshu(int shu, int v) {
cout << "调用函数, id = " << this_thread::get_id() << endl;
//Sleep(5000);
string t = to_string(shu);
return t;
}
int main() {
std::packaged_task<string(int, int)> pack1(hanshu);
//future<string> future1 = pack1.get_future(); //如果是move的话要放在他前面哦
thread thread1(ref(pack1), 3, 3);
thread1.join();
future<string> future1 = pack1.get_future(); //先调用packaged_task中的get_future函数将其绑定在future1中。
//shared_future<string> future1 = pack1.get_future(); //不经过future直接绑定到shared_future上
shared_future<string>future2(move(future1)); //先绑定到future上,再移动到shared_future上,注意,移动后future就没有绑定线程,即不能get了
//cout << future1.get() << endl; //通过get获取返回值
cout << future2.get() << endl; //通过get获取返回值
return 0;
}
promise :
promise在头文件<future>中,是一个类模板,可以在一个线程中保存某个值(使用set_value()),然后在另一个线程中取出(使用get_future()绑定后通过get()取出),常用的成员函数有set_value();
#include<iostream>
#include<thread>
#include<future>
using namespace std;
void hanshu1(promise<int> &cunchu, int a) { //由于要存储值所以要用引用
cunchu.set_value(a);
}
void hanshu2(future<int> &future1) {
int a = future1.get();
cout << "hanshu1线程中存储的数是" << a << endl;
}
int main() {
promise<int> cunchu;
thread thread1(hanshu1, ref(cunchu), 3); //形参有引用,实参用ref
future<int> future1 = cunchu.get_future(); //绑定
thread thread2(hanshu2, ref(future1));
thread1.join();
thread2.join();
return 0;
}