文章目录
std::async、std::future创建后台任务并返回值
std::async
std::async
是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,它返回一个std::future
对象,这个对象是个类模板。
异步任务:
自动创建一个线程,并开始 执行对应的线程入口函数,它返回一个std::future对象,这个std::future对象中就含有线程入口函数所返回的结果(其实就是线程执行的结果),我们可以通过调用future对象的成员函数get()来获取结果。
std::future
std::future
提供了一种访问异步操作结果的机制,就是说这个结果你可能没办法马上拿到,但是在不久的将来,这个线程执行完毕的时候,你就能够拿到结果了,所以,可以理解未:future中保存着一个值,这个值是在将来的某个时刻能够拿到。
-
std::future.get()
成员函数会等待线程执行结束并返回结果,拿不到结果它就会一直等待,感觉有点像join()但是,它是可以获取结果的。 -
std::future.wait()
成员函数,用于等待线程返回,本身并不返回结果,这个效果和 std::thread 的join()更像。
代码示例:
#include <future>
//线程入口函数
int myThread() {
cout << "thread begin with id =" << this_thread::get_id() << endl; //打印线程id
chrono::microseconds dura(5000); //休息5s
this_thread::sleep_for(dura); //休息了一定时间
cout << "thread end with id =" << this_thread::get_id() << endl; //打印线程id
return 5;
}
int main() {
cout << "main thread id = " << this_thread::get_id() << endl;
future<int> result = async(myThread); //创建线程,入口函数为myThread
cout << result.get() << endl; //调用future的get成员函数获取结果并打印
//如果myThread没执行完,会一直在这里等待,直到子进程执行结束,拿到返回结果
return 0;
}
- 包含头文件
#include < future >
-
future<int> result = async(myThread);
创建一个线程,入口函数为myThread,future类模板,返回结果类型为int -
result.get()
获得返回的结果,如果myThread没执行完,会一直在这里等待,直到子进程执行结束,拿到返回结果。(因此必须子线程的入口函数必须有返回值,不然主进程会一直卡在这里) -
get()
成员函数只能调用一次,调用多次会报错。 - 如果不调用
get()
,主线程也会在return 0;
之前一直等待子进程执行完毕(但是推荐使用get,这样写出的程序稳定)
std::launch
我们通过向std::async()
传递一个参数,该参数是std::launch
类型(枚举类型),来达到一些特殊的目的:
1.std::lunch::deferredstd::lunch::deferred
表示线程入口函数的调用会被延迟,一直到std::future的wait()或者get()函数被调用时(由主线程调用)才会执行;如果wait()或者get()没有被调用,则不会执行。
实际上根本就没有创建新线程。std::lunch::deferred意思时延迟调用,并没有创建新线程,是在主线程中调用的线程入口函数。
代码示意:
#include <future>
//用类作线程入口函数
class MyThread {
public:
int myThread(int val) {
cout << "thread begin with id =" << this_thread::get_id() << endl; //打印线程id
cout << "thread val =" << val << endl;
chrono::microseconds dura(5000); //休息5s
this_thread::sleep_for(dura); //休息了一定时间
cout << "thread end with id =" << this_thread::get_id() << endl; //打印线程id
return 5;
}
};
int main() {
cout << "main thread id = " << this_thread::get_id() << endl;
// future<int> result = async(myThread); //创建线程,入口函数为myThread
MyThread a;
int tempval = 12;
future<int> result = async(std::launch::deferred, &MyThread::myThread, &a, tempval); //创建线程,用类的成员函数作线程入口函数,注意传参的个数
cout << result.get() << endl; //调用future的get成员函数获取结果并打印
//如果myThread没执行完,会一直在这里等待,直到子进程执行结束,拿到返回结果
return 0;
}
- 这里是用的类的成员函数作线程入口函数:注意async的调用:
future<int> result = async(std::launch::deferred, &MyThread::myThread, &a, tempval);
参数的传参顺序与方式 - 可以看到结果,子进程与主进程的id一样,说明根本没有创建新进程;
所以deferred实际上就是延迟调用,不创建新线程,在主线程中执行入口函数。
2.std::launch::async
在调用async函数的时候就开始创建新线程。也就是说,不用等到调用get,在创建future的时候,就已经可以运行新线程了。
MyThread a;
int tempval = 12;
future<int> result = async(std::launch::async, &MyThread::myThread, &a, tempval); //创建线程,用类的成员函数作线程入口函数,注意传参的个数
cout << "loading!" << endl;
cout << "loading!" << endl;
cout << "loading!" << endl;
cout << "loading!" << endl;
cout << "loading!" << endl;
cout << result.get() << endl; //调用future的get成员函数获取结果并打印
//如果myThread没执行完,会一直在这里等待,直到子进程执行结束,拿到返回结果
可以用这段代码测试,可以发现,main还在打印loading的时候,就已经进入myThread开始执行了。
std::packaged_task
packaged_task
:打包任务,把任务包装起来。它也是一个类模板,它的模板参数是各种可调用对象,通过packaged_task把各种可调用对象包装起来,方便将来作为线程入口函数来调用。
packaged_task<int(int)> mypt(myThread); //<int(int)>表示一个返回值为int,参数为int的函数,用packaged_task进行包装
thread mythread(ref(mypt), 1); //创建一个新线程,线程入口函数为packaged_pack包装的,参数为1的函数
mythread.join();
future<int> result = mypt.get_future();//std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
cout << result.get() << endl;
packaged_task
包装一个lamda表达式:
int main() {
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 mythread(std::ref(mypt), 1);
mythread.join();
std::future<int> result = mypt.get_future();
//std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
cout << result.get() << endl;
return 0;
}
std::promise
std::promise
也是一个类模板,能够在某个线程中给它赋值,然后我们可以在其他线程中,把这个值取出来。
void myThread(promise<int> &temp, int calc) {
//做一系列运算
calc++;
chrono::microseconds dura(5000); //休息5s
this_thread::sleep_for(dura); //休息了一定时间,用sleep模拟运算时间
//计算结果
int result = calc;
temp.set_value(result); //用set_value把结果保存到promise对象中
}
int main() {
cout << "main thread id = " << this_thread::get_id() << endl;
promise<int> myprom; //实例化一个promise对象,保存值为int
thread mythread(myThread, ref(myprom), 180); //注意传参:线程入口函数,参数1,参数2
mythread.join();
//获取结果值
future<int> result = myprom.get_future(); //promise和future绑定,用于获取返回值
cout << result.get() << endl;
return 0;
}
- 通过promise保存一个值,在将来某个时刻我们通过吧一个future绑定到这个promise上,来得到绑定的值;
- 注意使用thread时,必须 join() 或者 detach() 否则程序会报异常;
- 因此,我们可以通过promise + future的方式实现两个线程数据的交换,线程2可以使用线程1计算出的结果。
对于3,代码如下:
//myThread见上面代码
void myThread2(future<int> &temp) {
auto result = temp.get();
cout << "thread2 result = " << result << endl;
}
int main() {
promise<int> myprom; //实例化一个promise对象,保存值为int
thread mythread(myThread, ref(myprom), 180); //注意传参:线程入口函数,参数1,参数2
mythread.join();
//获取结果值
future<int> result = myprom.get_future(); //promise和future绑定,用于获取返回值
//线程2使用线程1计算出的结果
thread mythread2(myThread2, ref(result));
mythread2.join();
return 0;
}
最后:
-
我们学习这些东西的目的并不是,要把他们都用到实际开发中。
-
相反,如果我们能够用最少的东西写出一个稳定的,高效的多线程程序,更值得赞赏。
-
我们为了成长必须阅读一些高手写的代码,从而实现自己代码的积累;