一、std::future期望值的三处用法:
1、期望值std::future<T>与承诺值std::promise<T>搭配使用,执行"std::future<T> fu=pro.get_future();"绑定承诺值与期望值,承诺值pro.set_value()或pro.set_exception()传值后,由期望值fu.get()提取,或fu.wait()阻塞消费者线程直到期望值就绪;
2、期望值std::future<T>与打包任务std::packaged_task(func,args)搭配使用,执行"task.get_future()"绑定期望值与任务;任务入口函数执行完毕后,返回值返回给期望值,由期望值fu.get()提取,或fu.wait()阻塞消费者线程直到期望值就绪;
3、期望值std::future<T>与异步任务std::async()搭配使用,执行"std::future<T> fu=std::async(func,args)"绑定期望值与异步任务。异步任务入口函数执行完毕后,返回值返回给期望值,由期望值fu.get()提取,或fu.wait()阻塞消费者线程直到期望值就绪;
二、将异常存于期望值中——《C Concurrency in Actino 2》-4.2.4
对应上面三种用法:
1、当向承诺值std::promise<T>存入的是一个异常而非一个数值时,需要调用std::promise<T>::set_exception()成员方法,而非set_value()。这通常是用在一个try{}catch(){}块中,并作为算法的一部分。向承诺值写入一个异常的代码参考:
i、std::current_exception()函数引用上文作用域抛出的异常;
ii、承诺值写入的异常将在期望值试图get()提取时在期望值所在的消费者线程中抛出
#include<iostream>
#include<future>
#include<chrono>
#include<functional>
#include<exception>
using F=std::function<int(int,int,int&)>;
//等同于typedef std::function<int(int,int,int&)>;
int Test_func(int a,int b,int &c){
c=a+b;
return c;
};
void Productor(std::promise<F>&p){
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout<<"生产者向承诺值传值"<<"\n";
//2、std::current_exception()检索"抛出的异常"
try{
throw std::logic_error("就是一个异常");
}catch(std::exception &e){
p.set_exception(std::current_exception()); //std::current_exception()函数引用上文作用域抛出的异常
}
}
template<typename T,typename...Args>
void Consumer(std::future<T>&f,Args&&...args){
try{
auto fun=f.get(); //阻塞提取,承诺值写入的异常将在期望值试图get()提取时在期望值所在的消费者线程中抛出
auto fres=fun(std::forward<Args>(args)...);
std::cout<<"消费者提取期望值,传递的函数运行结果:"<<fres<<"\n";
}catch(std::logic_error &exp){
std::cout<<"消费者线程捕获到期望值get()返回的异常:"<<exp.what()<<"\n";
}
}
int main(int argc,char*argv[]){
std::promise<F> pr1; //声明一个承诺值,将传入生产者线程
std::future<F> fu1=pr1.get_future(); //声明一个期望值绑定到承诺值,将传入消费者线程
int res(0);
std::thread t1(Consumer<F,int,int,int&>,std::ref(fu1),1,2,std::ref(res));
std::thread t2(Productor,std::ref(pr1));
t1.join();
t2.join();
}
macOS执行输出:
HaypinsMBP:MultiThread haypin$ clang++ -g std-promise.cpp -std=c++17 -o main
HaypinsMBP:MultiThread haypin$ ./main
生产者向承诺值传值
消费者线程捕获到期望值get()返回的异常:就是一个异常
HaypinsMBP:MultiThread haypin$
2、将函数打包进std::packaged_task<func_type,args_type>任务包后,打包函数如果抛出一个异常,这个异常将被返回给期望值(从而期望值状态被设置为"就绪"),在期望值调用get()时再次抛出,就像上面生产者线程中向承诺值写入异常,然后消费者线程中期望值调用get()会抛出异常一样。
3、函数作为std::async<func_type,args_type>的一部分时,当异步任务函数链抛出一个异常时,这个异常将返回到期望值,之后期望值的状态被设置为"就绪",然后期望值调用get()会抛出这个异常。
#include<iostream>
#include<thread>
#include<future>
#include<unistd.h>
#include<time.h>
double square(doublex){
time_t nowt=time(nullptr);
char* nowtc=new char[1<<10];
nowtc=ctime(&nowt);
std::cout<<nowtc<<"square thread echo\n";
try{
if(x<0){
throw std::out_of_range("exception:square minus!");
}
}catch(std::exception &exp){
std::cout<<"except in square thread:"<<&exp<<"\n";
throw exp;
}
return sqrt(x);
};
int main(int argc,char *argv[]){
std::future<double>f=std::async(square,-1);
sleep(1);
std::cout<<"main thread echo\n";
try{
//double y(square(-1));
std::cout<<f.get()<<"...\n";
/*异步线程产生的异常将传递到主调线程中调用get()获取期望结果的执行中,从而实现在主调线程捕获异步子线程产生的异常*/
}catch(std::exception &exp){
std::cout<<"except in main thread:"<<&exp<<"\n";
std::cout<<exp.what()<<"\n";
}
}
macOS执行输出:
HaypinsMBP:MultiThreadhaypin$./main
Thu Jan 28 21:35:39 2021
square thread echo
except in square thread:0x7fb88e6043b0
main thread echo
except in main thread:0x7fb88e604490
std::exception
HaypinsMBP:MultiThreadhaypin$
注意:标准并没有指定重新抛出的这std::exception异常对象是原始的异常对象还是一个拷贝;从上面macOS上执行结果看出,消费者线程中期望值get()到的异常是生产者async线程写入的异常的拷贝。