多线程07:async、future、packaged_task、promise
async、future、packaged_task、promise
本节内容需要包含头文件:#include <future>
一、std::async、 std::future 创建后台任务并返回值
①:启用async
- std::async是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,它返回一个std::future对象,这个对象是个类模板。
- “启动一个异步任务”:就是自动创建一个线程,并开始 执行对应的线程入口函数,它返回一个std::future对象,这个std::future对象中就含有线程入口函数所返回的结果,我们可以通过调用future对象的成员函数get()来获取结果。
- “future”将来的意思,也有人称呼std::future提供了一种访问异步操作结果的机制,就是说这个结果你可能没办法马上拿到,但是在不久的将来,这个线程执行完毕的时候,你就能够拿到结果了,所以,大家这么理解:future中保存着一个值,这个值是在将来的某个时刻能够拿到。
- std::future对象的get()成员函数会等待线程执行结束并返回结果,拿不到结果它就会一直等待,感觉有点像join()。但是,它是可以获取结果的。并且记得一个future只能调用一次get(无论哪里,都会将数据转移),调用两次程序会崩溃。
- std::future对象的wait()成员函数,用于等待线程返回,本身并不返回结果,这个效果和 std::thread 的join()更像。没有只能调用一次限制,但是调用多也没有效果。
- 如果不调用future的get()成员函数和wait()成员函数,程序也会在主函数return处一直等待调用子线程结束;
看例子:
#include <iostream>
#include <future>
using namespace std;
class A {
public:
int mythread(int data) {
cout << data << endl;
return data * 2;
}
};
int mythread() {
cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(5000);//睡5秒
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; //卡在这里等待mythread()执行完毕,拿到结果
//类成员函数
std::future<int> result2 = std::async(&A::mythread, &a, tmp); //第二个参数是对象引用才能保证线程里执行的是同一个对象
//cout << result2.get() << endl;//get()只能调用一次,result2不再有结果值,转移的是存的数据类型,这里是int!
//或者result2.wait();
cout << "finish all" << endl;
return 0;
}
②:std::async的第一个参数
- 参数:std::launch::deferred: 表示线程入口函数调用被延迟到std::future的wait()或者get()函数调用时才执行; 那如果wait()或者get()没调用,那么线程没执行!线程没有创建!是在主线程中调用的线程入口函数!
#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; //卡在这里等待mythread()执行完毕,拿到结果
cout << "finish all" << endl;
return 0;
}
- 参数:std::launch::async:在调用async函数的时候就开始创建线程;会强制std::async创建新线程,和thread一样。
- 带上两个参数:std::launch::async |std::launch::deferred 这里这个 |:以为这调用async的行为可能是 创建新线程并立即执行,或者没有创建新线程并且延迟调用result.get()才开始执行任务入口函数,两者居其一。(系统会自行决定是异步(创建新线程)还是同步(不创建新线程)方式运行),默认值就是这个!
二、std::package_task
- 目的就是:打包任务,把任务包装起来
- std::packaged_task是个模板类,它的模板参数是各种可调用对象(函数,函数指针,lambda表达式,bind创建的对象,以及重载了函数调用符的类,有点像function);通过std::packaged_task来把各种可调用对象包装起来,方便将来作为线程入口函数来调用。
- packaged_task包装起来的可调用对象还可以直接调用,所以从这个角度来讲,packaged_task对象,也是一个可调用对象。
- 看例子:
//可调用对象是普通函数
#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;
//我们把函数mythread通过packaged_task包装起来
//参数是一个int,返回值类型是int
std::packaged_task<int(int)> mypt(mythread);
std::thread t1(std::ref(mypt), 1);//传入真引用!不可以用detach模式
t1.join();
std::future<int> result = mypt.get_future();
//std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
cout << result.get() << endl;
return 0;
}
//可调用对象是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;
});
std::thread t1(std::ref(mypt), 1);//传真引用,因为是packege_task,mypt只能再被调用一次
t1.join();
std::future<int> result = mypt.get_future();
cout << result.get() << endl;
cout << "finish overhead" << endl;
return 0;
}
//调用对象是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);//也可以在主线程中直接调用,这样就和function的效果一样,但是只能调用一次
std::future<int> result = mypt.get_future();
cout << result.get() << endl;
cout << "finish overhead" << endl;
return 0;
}
tip:了解ref
三、promise
std::promise 类模板,我们能够在某个线程中给它赋值,然后我们可以在其他线程中把这个值取出来用;
总结:通过promise保存一个值,在将来某时刻我们通过把一个future绑定到这个promise上来得到这个绑定的值;
#include<iostream>
#include<thread>
#include<mutex>
#include<future>
using namespace std;
void mythread(std::promise<int>&tmpp, int calc)
{
//做一系列复杂的操作
calc++;
calc *= 10;
//做其他运算,比如整整花费了5秒钟
std::chrono::milliseconds dura(5000); //定一个5秒的时间
std::this_thread::sleep_for(dura); //休息一定时常
int result = calc; //保存结果
tmpp.set_value(result); //####1.结果保存到了tmpp这个promise对象中
}
void mythread2(std::future<int> &tmpf)
{
auto result = tmpf.get();
cout <<"mythread result = " << result<<endl;
}
int main()
{
std::promise<int> myprom; //声明一个std::promise对象myprom,保存的值类型为int;
std::thread t1(mythread,std::ref(myprom),180);
t1.join();
//获取结果值
std::future<int> fu1 = myprom.get_future();//####2.promise与future绑定,用于获取线程返回值
std::thread t2(mythread2,std::ref(fu1));
t2.join(); //等mythread2执行完毕
cout << "finish all" << endl;
//fu1不再有用,已经为空;已经全部传入mythread2的tmpf中,但是myprom还存着值1810;
return 0;
}
//对于ref,package_task、future都会移动后原本的变量就没有内容了,对于promise,却还有,要探索一下
四、std::asyn深入理解
4.1 std::async参数详述
延迟调用参数 std::launch::deferred【延迟调用】,std::launch::async【强制创建一个线程】;
std::async()我们一般不叫创建线程(他能够创建线程),我们一般叫它创建一个异步任务。std::async和std::thread最明显的不同:就是 async 有时候并不创建新线程。
①如果用std::launch::deferred 来调用async?
延迟到调用 get() 或者 wait() 时执行,如果不调用就不会执行
②如果用std::launch::async来调用async?
强制这个异步任务在新线程上执行,这意味着,系统必须要创建出新线程来运行入口函数。
③如果同时用 std::launch::async | std::launch::deferred
这里这个 | 意味着async的行为可能是 std::launch::async 创建新线程立即执行, 也可能是 std::launch::deferred 没有创建新线程并且延迟到调用get()执行,由系统根据实际情况来决定采取哪种方案
④不带额外参数 std::async(mythread),只给async 一个入口函数名,此时的系统给的默认值是 std::launch::async | std::launch::deferred 和 ③ 一样,有系统自行决定异步还是同步运行。
4.2 std::async和std::thread()区别
- std::thread()如果系统资源紧张可能出现创建线程失败的情况,如果创建线程失败那么程序就可能崩溃,而且不容易拿到函数返回值(不是拿不到)
- std::async()创建异步任务。可能创建线程也可能不创建线程,并且容易拿到线程入口函数的返回值;
由于系统资源限制:
①如果用std::thread创建的线程太多,则可能创建失败,系统报告异常,崩溃。
②如果用std::async,一般就不会报异常,因为如果系统资源紧张,无法创建新线程的时候,async不加额外参数的调用方式就不会创建新线程。而是在后续调用get()请求结果时执行在这个调用get()的线程上。
如果你强制async一定要创建新线程就要使用 std::launch::async 标记。承受的代价是,系统资源紧张时可能崩溃。
③根据经验,一个程序中线程数量 不宜超过100~200 。
4.3 async不确定性问题的解决
让系统自行决定是否创建线程:std::future<int> result = std::async(mythread);
问题焦点在于这个写法,任务到底有没有被推迟执行!
如何判断:通过future中wait_for()方法的返回值;详情看下一节内容
多阅读高手代码,多积累,提升自己技术!
多线程07:async、future、packaged_task、promise的更多相关文章
- C++多线程の线程通信future,promise,async
- c++多线程基础5(future,async,packaged_task,promise)
以下内容整理自:https://www.cnblogs.com/my_life/articles/5401190.html future 是一个能从其他地方获取到一个值的对象,如果是在不同的线程中,则 ...
- The promises and challenges of std::async task-based parallelism in C++11 C++11 std::async/future/promise
转载 http://eli.thegreenplace.net/2016/the-promises-and-challenges-of-stdasync-task-based-parallelism- ...
- c/c++ 多线程 等待一次性事件 std::promise用法
多线程 等待一次性事件 std::promise用法 背景:不是很明白,不知道为了解决什么业务场景,感觉std::async可以优雅的搞定一切的一次等待性事件,为什么还有个std::promise. ...
- c/c++ 多线程 等待一次性事件 packaged_task用法
多线程 等待一次性事件 packaged_task用法 背景:不是很明白,不知道为了解决什么业务场景,感觉std::asynck可以优雅的搞定一切,一次等待性事件,为什么还有个packaged_tas ...
- async/await actor promise 异步编程
Python协程:从yield/send到async/await http://blog.guoyb.com/2016/07/03/python-coroutine/ Async/Await替代Pro ...
- Netty 中的异步编程 Future 和 Promise
Netty 中大量 I/O 操作都是异步执行,本篇博文来聊聊 Netty 中的异步编程. Java Future 提供的异步模型 JDK 5 引入了 Future 模式.Future 接口是 Java ...
- C++之future和promise
future和promise的作用是在不同线程之间传递数据.使用指针也可以完成数据的传递,但是指针非常危险,因为互斥量不能阻止指针的访问:而且指针的方式传递的数据是固定的,如果更改数据类型,那么还需要 ...
- Async/Await替代Promise的6个理由
译者按: Node.js的异步编程方式有效提高了应用性能:然而回调地狱却让人望而生畏,Promise让我们告别回调函数,写出更优雅的异步代码:在实践过程中,却发现Promise并不完美:技术进步是无止 ...
随机推荐
- 5. Git初始化及仓库创建和操作
4. Git初始化及仓库创建和操作 基本信息设置 1. 设置用户名 git config --global user.name 'itcastphpgit1' 2. 设置用户名邮箱 git confi ...
- centos下用ffmpeg推流宇视科技摄像头rtsp流到前端播放(无flash)
严禁垃圾中文技术网站复制粘贴 流程:安装SRS服务接收ffmpeg的推流,SRS会提供一个flv的播放地址,前端通过fls.js播放即可,无需flash. 1.安装ffmpeg 提供两个版本,都能推流 ...
- d面试题汇总
HTML Doctype作用,HTML5 为什么只需要写<!DOCTYPE HTML>? html5有哪些新特性?移除了哪些元素? 简述一下你对HTML语义化的理解? 行内元素有哪些,块级 ...
- 一个用于学习的react项目
React-element 根据开源项目 vue-sell进行的开发,将其改造成了react的项目.在开始学习vue的时候就是用的这个项目,发现效果不错,所以在学习React也使用了此项目. 目的:将 ...
- 前端眼里的docker
docker是什么 可以简单的认为docker容器是一个虚拟机,封装就是把这个虚拟机打包,打包后能在任何系统跑,docker装上即用.也可以形象的比喻成一个集装箱,把所有货物都打包好放到箱子里,不需要 ...
- java中Super指向他紧邻的父类,而不是最底层的基类
3.2 当有两次继承时,演示super指向他紧邻的父类 我们把上面的例子扩展成两次继承, 就看出:马克-to-win,Super是一个参考(或说指针)指向他紧邻的父类,而不是最底层的基类. 例1.3. ...
- Androd点击一个选框取消其他选框
说明: 我做的体温填报系统需要在填报体温时勾选有无特殊情况 当勾选'无'时需要将用户勾选的其他选框取消 当勾选其他选框时需要将'无'这个选框取消 效果: 代码: addTem.xml <Line ...
- LazyCaptcha自定义随机验证码和字体
介绍 LazyCaptcha是仿EasyCaptcha和SimpleCaptcha,基于.Net Standard 2.1的图形验证码模块. 目前Gitee 52star, 如果对您有帮助,请不吝啬点 ...
- python---单链表的常用操作
class Node(object): """结点""" def __init__(self, data): self.data = dat ...
- spring配置数据源(加载properties文件)
1.在spring中引入properties配置文件需要引入context的命名空间和真实地址 2.然后加载文件 需要注意的是这是采用的是set注入方式,所以name属性值必须是连接池set方法名去掉 ...