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,却还有,要探索一下

再了解async\future\等

四、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的更多相关文章

  1. C++多线程の线程通信future,promise,async

  2. c++多线程基础5(future,async,packaged_task,promise)

    以下内容整理自:https://www.cnblogs.com/my_life/articles/5401190.html future 是一个能从其他地方获取到一个值的对象,如果是在不同的线程中,则 ...

  3. 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- ...

  4. c/c++ 多线程 等待一次性事件 std::promise用法

    多线程 等待一次性事件 std::promise用法 背景:不是很明白,不知道为了解决什么业务场景,感觉std::async可以优雅的搞定一切的一次等待性事件,为什么还有个std::promise. ...

  5. c/c++ 多线程 等待一次性事件 packaged_task用法

    多线程 等待一次性事件 packaged_task用法 背景:不是很明白,不知道为了解决什么业务场景,感觉std::asynck可以优雅的搞定一切,一次等待性事件,为什么还有个packaged_tas ...

  6. async/await actor promise 异步编程

    Python协程:从yield/send到async/await http://blog.guoyb.com/2016/07/03/python-coroutine/ Async/Await替代Pro ...

  7. Netty 中的异步编程 Future 和 Promise

    Netty 中大量 I/O 操作都是异步执行,本篇博文来聊聊 Netty 中的异步编程. Java Future 提供的异步模型 JDK 5 引入了 Future 模式.Future 接口是 Java ...

  8. C++之future和promise

    future和promise的作用是在不同线程之间传递数据.使用指针也可以完成数据的传递,但是指针非常危险,因为互斥量不能阻止指针的访问:而且指针的方式传递的数据是固定的,如果更改数据类型,那么还需要 ...

  9. Async/Await替代Promise的6个理由

    译者按: Node.js的异步编程方式有效提高了应用性能:然而回调地狱却让人望而生畏,Promise让我们告别回调函数,写出更优雅的异步代码:在实践过程中,却发现Promise并不完美:技术进步是无止 ...

随机推荐

  1. vsftd及虚拟用户

    临时需要搭建一个ftp,突然忘记怎么搞了,重新整一下,以后备用 vsftd及虚拟用户 1.安装vsftpd yum install vsftpd 2.添加用户(用于虚拟用户映射) adduser se ...

  2. docker学习-01-安装docker

    [root@localhost firstDocker]# cat /etc/centos-release CentOS Linux release 7.6.1810 (Core) [root@loc ...

  3. ES6技术之“判空方案”

    一.常用方案 直接判断 代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  4. 1、Jetson Nano 远程桌面XP问题

    jeston nano上网 方法3(最简单的方法) 最简单的方法真的特简单,用USB数据线连接主板的USB接口以及手机,打开手机的USB共享即可,若要使用静态IP,可在主板上修改配置文件,接口一般为u ...

  5. Pandas查询数据的几种方法

    Pandas查询数据 Pandas查询数据的几种方法 df.loc方法,根据行.列的标签值查询 df.iloc方法,根据行.列的数字位置查询 df.where方法 df.query方法 .loc既能查 ...

  6. shell脚本编程(一) 变量、条件判断、循环

    目录   1. shell脚本编程   2. 运行 Shell 脚本有两种方法   3. 变量   4. 本地变量   5. 环境变量   6. 参数变量   7. 多行注释   8. if条件判断 ...

  7. hanoi(汉诺塔)递归实现

    汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘.大梵天命令婆罗门把圆盘从下面开始按大小顺序 ...

  8. 前端网络安全——前端CSRF

    CSRF:Cross Site Request Forgy(跨站请求伪造) 用户打开另外一个网站,可以对本网站进行操作或攻击.容易产生传播蠕虫. CSRF攻击原理: 1.用户先登录A网站 2.A网站确 ...

  9. hdfs对文件的增删改查

    源代码: pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&qu ...

  10. C++五子棋(三)——判断鼠标有效点击

    分析 在鼠标左键点击时,我们不能让新棋子在已有棋子的位置落下,同时我们还要让棋子在规定位置落下--棋盘线的交点处. 功能实现 创建数据类型 创建头文件chessData.h和源文件chessData. ...