C++异步(1)
什么是异步?
异步就是多个线程是同时执行的,与之相对的就是线程同步,二者都应用在并发的场景上。
异步的特点
异步执行的任务无需等待其他任务完成,其本身是通过非阻塞的方式执行的,不依赖前驱任务,通常用于IO密集型场景。
非阻塞:线程不会被其他线程阻塞;
回调或事件驱动:
1、可以通过最直接采用策略执行回调函数,自动创建和管理线程;
2、还可以通过primise收到那个执行线程设置future的异步结果;
未来类--future
future简单来说就是存储线程结果的一个模版类,我们可以通过其get方法获取其异步编程结果的值;
async策略
执行策略:launch::async代表的是立刻在当前线程处执行异步回调函数;
//执行线程回调函数
double func(){cout<<"我是线程回调函数"<<endl; return 6.6;
}void testfuture(){future<double> ret=async(launch::async,func);//立即执行的策略cout<<"开始睡眠"<<endl;sleep(2);auto res=ret.get();cout<<"future:"<<res<<endl;
}
其中ret接收的就是异步线程回调函数的返回值;
deferred策略
该执行策略代表的是延迟执行,也就是主线程执行到async函数时不会立刻触发执行异步回调函数,而是在后续调用future的get方法时才会触发执行回调函数;
#pragma once
#include <future>
#include <iostream>
#include <unistd.h>
using namespace std; //执行线程回调函数
double func(){cout<<"我是线程回调函数"<<endl; return 6.6;
}void testfuture2(){future<double>ret=async(launch::deferred,func);//延迟执行策略cout<<"开始睡眠"<<endl;sleep(2);auto res=ret.get();cout<<"future:"<<res<<endl;
}
这段程序的执行结果就是先打印“开始睡眠“,然后睡眠2s,执行到get处,才执行回调函数,打印"我是线程回调函数",返回res获取返回值6.6,最后打印"future:6.6";
Promise(承诺未来值)
Promise也是一个模版类,通常配合future使用,用于在线程间传递数据或同步异步操作的结果
解决的问题
(1) 线程间结果的传递
普通线程函数只能通过参数传递输入数据,但无法直接返回计算结果(除非使用全局变量或指针,但这不够安全)。
promise
提供了一种安全、标准化的方式,让一个线程(生产者)可以设置一个值,而另一个线程(消费者)可以通过future
获取这个值。(2) 异步操作的同步
在异步编程中,我们可能需要等待某个线程完成任务并获取其结果,而
promise/future
提供了一种线程安全的等待机制,避免了手动使用条件变量或锁。(3) 异常传递
如果一个线程在执行过程中抛出异常,可以通过
promise
将异常传递给future
,让调用方能够捕获并处理它。
实现方案
promise+move
#include <iostream>
#include <future>
#include <thread>using namespace std;void task(promise<int> pro) {pro.set_value(666);
}int main() {promise<int>ff;//绑定future和promisefuture<int>fut = ff.get_future();thread t(task,move(ff));//获取结果cout <<"异步future:"<< fut.get() << endl;t.join();return 0;
}
这种方案是比较安全的,因为没有使用指针和引用,关键点在于,中间变量作为参数,回调执行完就会释放掉参数pro;
那么问题来了,回调执行完就会释放掉pro,那future还能获取结果吗?
答案是可以的!future从promise读取结果的过程在于set_value写入,执行回调之前future和prommise已经绑定了共享的关系,而set_value就会同步数据给future;即便是后续参数pro和主线程的ff都无效了,future依旧可以获取到正确的结果;
promise+ref
#include <iostream>
#include <future>
#include <thread>using namespace std;void task(promise<int>& pro) {pro.set_value(666);
}int main() {promise<int>ff;//绑定future和promisefuture<int>fut = ff.get_future();thread t(task,ref(ff));//获取结果cout <<"异步future:"<< fut.get() << endl;t.join();return 0;
}
与上面的区别在于回调的参数是引用,而是实参需要使用ref强调传递的是引用对象,这种方法因涉及引用,所以没有使用move安全一些;