C++多线程笔记2
std::async 函数模板 与 std::future 类模板
std::async()****
std::async()
是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,它返回一个 std::future
对象,std::future
是一个类模板
什么叫 “启动一个异步任务”
启动一个异步任务就是自动创建一个线程并开始执行对应的线程入口函数,它返回一个
std::future
对象这个对象里面就含有线程入口函数所返回的结果(线程返回的结果)我们可以通过调用
future
对象的成员函数get()
来获取结果函数中可以额外的传递一个参数
该参数类型是
std::lunnch
类型(枚举)来达到一些目的std::launch::deferred
表示线程入口函数调用被延迟到std::future
的wait()
或get()
函数时调用才执行,延迟调用,并且没有创建新线程,是在主线程中调用线程入口函数如果
wait()
或get()
没有被调用 则线程不会被执行std::launch::async
在调用async()
函数的时候就开始创建线程,不会等待get()
std::future
std::future
提供了一种访问异步操作结果的机制,就是说这个结果你可能没办法马上拿到,但是在不久的将来,这个线程执行完毕的时候,你就能够拿到结果了,所以,大家这么理解:future
中保存着一个值,这个值是在将来的某个时刻能够拿到。
std::future
对象的 get()
成员函数会等待线程执行结束并返回结果,拿不到结果它就会一直等待,感觉有点像 join()
。但是,它是可以获取结果的。
std::future
对象的 wait()
成员函数,用于等待线程返回,本身并不返回结果,这个效果和 std::thread
的 join()
更像。
具体实例
类定义
1 | class A { |
main函数
1 | int main() |
使用
std::launch::async
async()
强制创建了一个新的线程
使用
std::launch::deferred
则是在主线程中调用了这个函数,并没有创建新的线程
使用
std::launch::deferred | std::launch::async
这里这个
|
: 这意味着std::async
的行为可能是 创建新线程并立即执行 或者 创建新线程并且延迟调用系统自动决定异步还是同步方式运行
不带额外参数
与 3. 中效果完全一致
std::async和std::thread()区别
std::thread()
如果系统资源紧张可能出现创建线程失败的情况,如果创建线程失败那么程序就可能崩溃,而且不容易拿到函数返回值(不是拿不到)std::async()
创建异步任务。可能创建线程也可能不创建线程,并且容易拿到线程入口函数的返回值;
由于系统资源限制:
- 如果用
std::thread
创建线程太多,则可能创建失败,系统报告异常崩溃 - 如果用
std::async
一般就不会报异常不会崩溃,因为,如果系统资源紧张导致无法创建新线程的时候,std::async
这种不加额外参数的调用就不会创建新线程。而是后续调用了.get()
来请求结果,那么这个异步任务就运行在执行这条get()
语句所在的线程上
std::packaged_task 类模板
std::packaged_task
: 打包任务,把任务包装起来,可用于包装各种可调用对象的模板类
1 | // 线程入口函数 |
std::future
对象里包含有线程入口函数的返回结果,result
保存 mythread
的返回值.
我们也可以吧函数换成 lambda 表达式
1 | std::packaged_task<int(int)> mypt([](int mypar) { |
std::packaged_task
也是可调用对象
lambda 表达式的直接调用
1 | std::packaged_task<int(int)> mypt([](int mypar) { |
使用容器保存任务
1 | std::vector<std::packaged_task<int(int)>> my_tasks; |
std::promise 类模板
通过 promise
保存一个值,在将来某个时刻我们通过把一个 future
绑定到 promise
上来得到这个绑定的值
可以通过 promise
来实现两个线程中的数据传递
1 | std::promise<int> myprom; // 声明一个 std::promise 对象,保存值为int |