C++11异步编程
创始人
2024-06-01 14:29:59
0

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 1、std::future和std::shared_future
    • 1.1 std:future
    • 1.2 std::shared_future
  • 2、std::async
  • 3、std::promise
  • 4、std::packaged_task

前言

C++11提供了异步操作相关的类,主要有std:future、std:promise 和std:package task。std:future作为异步结果的传输通道,可以很方便地获取线程函数的返回值;。std:promise 用来包装一个值、将数据和future绑定起来,方便线程赋值; std:package_task 用来包装一个可调用对象,将函数和future绑定起来,以便异步调用。

1、std::future和std::shared_future

1.1 std:future

C++11中增加的线程,使得我们可以非常方便地创建和使用线程,但有时会有些不便,比如希望获取线程函数的返回结果,就不能直接通过thread.join()得到结果,这时就必须定义一个变量,在线程函数中去给这个变量赋值,然后执行join(),最后得到结果,这个过程是比较烦琐的。thread 库提供了future用来访问异步操作的结果,因为一个异步操作的结果不能马上获取,只能在未来某个时候从某个地方获取,这个异步操作的结果是一个未来的期待值,所以被称为future,future 提供了获取异步操作结果的通道。我们可以以同步等待的方式来获取结果,可以通过查询future 的状态( future_status) 来获取异步操作的结果。futrue_status有3种状态:

1、Deferred:异步操作还没开始
2、Ready:异步操作已经完成
3、Timeout:异步操作超时

future原型

template   future;
template  future;     
template <>         future

std::future是一个类模板

成员方法
在这里插入图片描述
通过future构造函数构造的future对象是无效的,此时调用future.get()可能会抛异常,需要我们捕获

int main()
{future fu;try{cout << fu.get() << endl;}catch (const std::exception& e){cout << e.what() << endl;}return 0;
}

在这里插入图片描述

如果没有初始化future,则表明它是无效的,此时我们也可以使用valid判断future对象是否有效

int main()
{future fu;if (fu.valid()){fu.get();}else{cout << "no state" << endl;}return 0;
}

std::asyanc是std::future的高级封装, 一般我们不会直接使用std::futrue,而是使用对std::future的高级封装std::async

mutex mtx;
string GetCurrentDateTime()
{SYSTEMTIME stime;GetLocalTime(&stime);stringstream ss;unique_lock lock(mtx);ss << "[" << this_thread::get_id() << "]" << "current time: " << stime.wYear << "/" << stime.wMonth \<< "/" << stime.wDay << "/" << stime.wHour << "/" << stime.wMinute << "/" << stime.wSecond << endl;this_thread::sleep_for(chrono::seconds(2));return ss.str();
}int main()
{future fu = async(GetCurrentDateTime);//执行一个异步任务来获取当前时间cout << this_thread::get_id() << " : main do something..." << endl;cout << fu.get() << endl;return 0;
}

在这里插入图片描述

上述代码中,main线程通过async创建一个新线程去执行异步任务(获取当前系统时间),在新线程执行异步任务的过程中,main线程可以不必阻塞,但在调用get方法时,如果新线程没有返回,就会被阻塞

我们可以通过wait_for进行超时等待返回结果,如果新线程还没返回,就做其他事情,再重复wait_for

mutex mtx;
string GetCurrentDateTime()
{SYSTEMTIME stime;GetLocalTime(&stime);stringstream ss;unique_lock lock(mtx);ss << "[" << this_thread::get_id() << "]" << "current time: " << stime.wYear << "/" << stime.wMonth \<< "/" << stime.wDay << "/" << stime.wHour << "/" << stime.wMinute << "/" << stime.wSecond << endl;this_thread::sleep_for(chrono::seconds(3));//休眠3秒return ss.str();
}int main()
{std::future_status status;future fu = async(GetCurrentDateTime);cout << this_thread::get_id() << " : main do something..." << endl;do{status = fu.wait_for(chrono::milliseconds(500));if (status == std::future_status::deferred){cout << "deferred:异步操作还没开始," << "main do something" << endl;}else if (status == std::future_status::timeout){cout << "timeout:在规定时间内异步操作还未完成," << "main do something" << endl;}else if (status == std::future_status::ready){cout << "ready:异步操作已经完成,当前时间为:" << fu.get();}} while (status != std::future_status::ready);return 0;
}

在这里插入图片描述

上述代码中,main线程调用了wait_for(chrono::milliseconds(500)),表示mian线程等待500ms,如果在500ms内,子线程完成异步任务,则立刻返回,如果超过500ms还没有完成异步任务,则不会继续等待,立刻返回

future不允许直接赋值或者拷贝,但允许移动赋值

void func(){}int main()
{future fu1;future fu2 = async(std::launch::async, func);if (fu2.valid()){cout << "fu2.valid()=true" << endl;cout << "move..." << endl;fu1 = std::move(fu2);cout << "fu2.valid()=" <cout << "fu2.valid()=false" << endl;}return 0;
}

在这里插入图片描述

get成功之后,future对象将不再有效

string func()
{return "fl";
}int main()
{future fu = async(std::launch::async, func);if (fu.valid()){cout << "fu.get() begin fu.valid()=" << fu.valid() << endl;cout << "fu.get()=" << fu.get() << endl;cout << "fu.get() after fu.valid()=" << fu.valid() << endl;}return 0;
}

在这里插入图片描述

1.2 std::shared_future

shared_futrue原型

template   shared_future;
template  shared_future; 
template <>         shared_future; 

C++11中的std: :shared_future是个模板类。与std::future类似,std::shared_future提供了一种访问异步操作结果的机制。不同于std::future,std::shared_futrue允许多个线程等待同一个共享状态。不同于std::future仅支持移动操作,std::shared_future既支持移动操作也支持拷贝操作,而多个std::shared_future对象可以引用相同的共享状态

前面谈到,对于std::future对象,如果就绪,使用get后,future对象就无效了,如果再使用get,则会抛异常。使用std::shared_future就很好的解决了这个问题

string func()
{return "fl";
}int main()
{shared_future fu = async(std::launch::async, func);if (fu.valid()){cout << "fu.get()=" << fu.get() << endl;cout << "fu.get()=" << fu.get() << endl;cout << "fu.get()=" << fu.get() << endl;}else{cout << "fu.valid()=false" << endl;}return 0;
}

在这里插入图片描述

支持拷贝和赋值

string func()
{return "fl";
}int main()
{shared_future fu = async(std::launch::async, func);shared_future fu1 = fu;shared_future fu2(fu1);if (fu.valid()){cout << "fu.get()=" << fu.get() << endl;cout << "fu1.get()=" << fu.get() << endl;cout << "fu2.get()=" << fu.get() << endl;}else{cout << "fu.valid()=false" << endl;}return 0;
}

在这里插入图片描述

2、std::async

std::async比std::future和std::promise、std::packaged和std::thread更高一层,它可以用来直接创建异步的task,异步任务返回的结果也保存在std::future中,需要获取异步任务的结果时,只需要调用future.get()方法即可,如果不关注异步任务的结果,只是简单地等待任务完成时的话,则调用future.wait()方法

async原型

template future::type>async (Fn&& fn, Args&&... args);template future::type>async (launch policy, Fn&& fn, Args&&... args);

后者比前者多了一个参数:launch policy,这个表示线程的创建策略,有两种策略,默认的策略是立即创建线程

  1. std::launch::async:在调用async时就开始创建线程
  2. std::launch::deferred:延迟加载方式创建线程。在调用async时不创建线程,知道调用了future的get或者wait时才创建线程

第二个参数:表示线程的入口函数,必须是一个可调用对象
第三个参数:表示线程函数的参数

std::async的操作,其实相当于封装了std::promise、std::packaged_task加上std::thread

使用std::launch::deferred作为参数

mutex mtx;
string GetCurrentDateTime()
{SYSTEMTIME stime;GetLocalTime(&stime);stringstream ss;unique_lock lock(mtx);ss << "[" << this_thread::get_id() << "]" << "current time: " << stime.wYear << "/" << stime.wMonth \<< "/" << stime.wDay << "/" << stime.wHour << "/" << stime.wMinute << "/" << stime.wSecond << endl;cout << "in GetCurrentDateTime()" << endl;return ss.str();
}int main()
{future fu = async(std::launch::deferred, GetCurrentDateTime);this_thread::sleep_for(chrono::milliseconds(500));cout << "main id is " << this_thread::get_id() << endl;cout << fu.get() << endl;return 0;
}

在这里插入图片描述
可以看到GetCurrentDateTime()也是有main线程执行的。在上述的执行顺序中,main线程先打印了自己的线程id,然后调用fu.get()时进入GetCurrentDateTime()内部,完成任务后才打印的时间。因此就能很好的证明deferred参数确实是在创建async时,不创建线程执行任务,而是在调用get方法时,由main线程去执行异步任务。

将参数改为async,执行结果为:

在这里插入图片描述

可以看到,在调用async时就开始创建线程去执行异步任务。

如果我们没有指定第一个参数,默认是async | deferred,具体采用哪个参数,取决于操作系统
在这里插入图片描述

3、std::promise

std::promise将数据和future绑定起来,为获取线程函数中的某个值提供便利,在线程函数中为外面传进来的promise赋值,在线程函数执行完成之后就可以通过promise的future获取该值了。值得注意的是:取值是间接地通过promise内部提供的future来获取的。

std::promise原型

template   promise;		 //空模板
template  promise;     //用于线程间交流对象
template <>         promise;   //用于交流无状态事件

成员方法
在这里插入图片描述

举个例子,简单了解一下promise和future是如何关联的

void func1(std::promise& promObj)
{cout << this_thread::get_id() << "设置promise的值" << endl;promObj.set_value(5);
}void func2(std::future& fut)
{cout << this_thread::get_id() << "访问promise的值" << endl;cout << this_thread::get_id() << ":" << fut.get() << endl;
}int main()
{std::promise prom;std::future fut = prom.get_future();std::thread t1(func1, std::ref(prom));std::thread t2(func2, std::ref(fut));t1.join();t2.join();return 0;
}

在这里插入图片描述

在上述代码中,我们定义一个std::promise prom,并让其与std::future fut进行关联。让t1线程去调用fun11,t2线程去调用func2。如果t2线程在func2中调用fut.get()时,而fut并没有被设置,此时f2线程会被阻塞,直到t1线程在func1中调用promObj.set_value(5),例如:

在这里插入图片描述
std::promise的operator=没有拷贝语义,即std::promise的普通赋值函数是被delete掉了,只有move语义,因此std::promise对象是禁止拷贝的

int main()
{std::promise prom;std::future fut = prom.get_future();std::promise prom1 = prom;return 0;
}

在这里插入图片描述

采用set_value_at_thread_exit演示一下死锁

mutex mtx;
void func1(std::promise& promObj)
{this_thread::sleep_for(chrono::microseconds(500));promObj.set_value_at_thread_exit(1);unique_lock lock(mtx);cout << this_thread::get_id() << "设置promise的值" << endl;
}void func2(std::future& fut)
{unique_lock lock(mtx);cout << this_thread::get_id() << "访问promise的值" << endl;cout << this_thread::get_id() << ":" << fut.get() << endl;
}int main()
{std::promise prom;std::future fut = prom.get_future();thread t1(func1, std::ref(prom));thread t2(func2, std::ref(fut));t1.join();t2.join();return 0;
}

在这里插入图片描述
为了让func2中的代码先执行,在func1的开始调用了sleep_for(chrono::microseconds(500)),休眠500微秒。首先f2线程拿到锁,执行到fut.get()会被阻塞,因为此时fut还未就绪。线程t1使用set_value_at_thread_exit(1),表明设置指定值为1,但是只有在t1线程结束时,才提醒。因此t1线程将被阻塞在unique_lock lock(mtx)上,而t2线程被阻塞在fut.get()上,所以产生了死锁。

使用set_value_at_thread_exit好处就是确保某个线程的退出,另一个线程才能被获取到某个值,例如:

void func1(std::promise& promObj)
{promObj.set_value(1);cout << this_thread::get_id() << "设置promise的值" << endl;cout << "do something..." << endl;cout << "do something..." << endl;cout << "do something..." << endl;cout << "do something..." << endl;
}void func2(std::future& fut)
{cout << this_thread::get_id() << "访问promise的值" << endl;cout << this_thread::get_id() << ":" << fut.get() << endl;
}int main()
{std::promise prom;std::future fut = prom.get_future();thread t1(func1, std::ref(prom));thread t2(func2, std::ref(fut));t1.detach();t2.join();return 0;
}

在这里插入图片描述
t1线程进行了分离,t2线程先结束,也就导致了main线程结束了,t1线程在后台执行,并没有将"do something…"打印到终端上。因此可以使用set_value_at_thread_exit使t2线程等待t1线程结束

在这里插入图片描述

一个线程将异常传递给另一个线程

void func1(std::promise& p)
{try{cout << this_thread::get_id() << " throw error" << endl;throw exception("this is func1 throw error");}catch (const std::exception& e){p.set_exception_at_thread_exit(std::current_exception());}
}void func2(std::future& fu)
{try{if (fu.valid())fu.get();}catch (const std::exception e){cout << this_thread::get_id() << " error is " << e.what() << endl;}
}int main()
{std::promise p;std::future fu = p.get_future();thread t1(func1, std::ref(p));thread t2(func2, std::ref(fu));t1.join();t2.join();return 0;
}

在这里插入图片描述

4、std::packaged_task

std:packaged _task 包装了一个可调用对象的包装类(如function、lambda expression、bind expression和another function object),将函数和future绑定起来,以便异步调用,它和std:promise在某种程度上有点像,promise保存了一个共享状态的值,而packaged task 保存的是一个函数。

std::packaged_task原型

template  packaged_task;     // undefined
template  class packaged_task;

成员函数

在这里插入图片描述

简单案例

int f(int x, int y) { return std::pow(x, y); }void task_lambda()
{std::packaged_task task([](int a, int b) {return std::pow(a, b);});std::future result = task.get_future();//auto result = task.get_future();task(2, 2);std::cout << "task_lambda:\t" << result.get() << '\n';
}void task_bind()
{std::packaged_task task(std::bind(f, 2, 3));std::future result = task.get_future();//auto result = task.get_future();task();std::cout << "task_bind:\t" << result.get() << '\n';
}void task_thread()
{std::packaged_task task(f);std::future result = task.get_future();std::thread task_td(std::move(task), 2, 4);task_td.join();std::cout << "task_thread:\t" << result.get() << '\n';
}int main()
{task_lambda();task_bind();task_thread();return 0;
}

在这里插入图片描述

相关内容

热门资讯

电视安卓系统哪个品牌好,哪家品... 你有没有想过,家里的电视是不是该升级换代了呢?现在市面上电视品牌琳琅满目,各种操作系统也是让人眼花缭...
安卓会员管理系统怎么用,提升服... 你有没有想过,手机里那些你爱不释手的APP,背后其实有个强大的会员管理系统在默默支持呢?没错,就是那...
安卓系统软件使用技巧,解锁软件... 你有没有发现,用安卓手机的时候,总有一些小技巧能让你玩得更溜?别小看了这些小细节,它们可是能让你的手...
安卓系统提示音替换 你知道吗?手机里那个时不时响起的提示音,有时候真的能让人心情大好,有时候又让人抓狂不已。今天,就让我...
安卓开机不了系统更新 手机突然开不了机,系统更新还卡在那里,这可真是让人头疼的问题啊!你是不是也遇到了这种情况?别急,今天...
安卓系统中微信视频,安卓系统下... 你有没有发现,现在用手机聊天,视频通话简直成了标配!尤其是咱们安卓系统的小伙伴们,微信视频功能更是用...
安卓系统是服务器,服务器端的智... 你知道吗?在科技的世界里,安卓系统可是个超级明星呢!它不仅仅是个手机操作系统,竟然还能成为服务器的得...
pc电脑安卓系统下载软件,轻松... 你有没有想过,你的PC电脑上安装了安卓系统,是不是瞬间觉得世界都大不一样了呢?没错,就是那种“一机在...
电影院购票系统安卓,便捷观影新... 你有没有想过,在繁忙的生活中,一部好电影就像是一剂强心针,能瞬间让你放松心情?而我今天要和你分享的,...
安卓系统可以写程序? 你有没有想过,安卓系统竟然也能写程序呢?没错,你没听错!这个我们日常使用的智能手机操作系统,竟然有着...
安卓系统架构书籍推荐,权威书籍... 你有没有想过,想要深入了解安卓系统架构,却不知道从何下手?别急,今天我就要给你推荐几本超级实用的书籍...
安卓系统看到的炸弹,技术解析与... 安卓系统看到的炸弹——揭秘手机中的隐形威胁在数字化时代,智能手机已经成为我们生活中不可或缺的一部分。...
鸿蒙系统有安卓文件,畅享多平台... 你知道吗?最近在科技圈里,有个大新闻可是闹得沸沸扬扬的,那就是鸿蒙系统竟然有了安卓文件!是不是觉得有...
宝马安卓车机系统切换,驾驭未来... 你有没有发现,现在的汽车越来越智能了?尤其是那些豪华品牌,比如宝马,它们的内饰里那个大屏幕,简直就像...
p30退回安卓系统 你有没有听说最近P30的用户们都在忙活一件大事?没错,就是他们的手机要退回安卓系统啦!这可不是一个简...
oppoa57安卓原生系统,原... 你有没有发现,最近OPPO A57这款手机在安卓原生系统上的表现真是让人眼前一亮呢?今天,就让我带你...
安卓系统输入法联想,安卓系统输... 你有没有发现,手机上的输入法真的是个神奇的小助手呢?尤其是安卓系统的输入法,简直就是智能生活的点睛之...
怎么进入安卓刷机系统,安卓刷机... 亲爱的手机控们,你是否曾对安卓手机的刷机系统充满好奇?想要解锁手机潜能,体验全新的系统魅力?别急,今...
安卓系统程序有病毒 你知道吗?在这个数字化时代,手机已经成了我们生活中不可或缺的好伙伴。但是,你知道吗?即使是安卓系统,...
奥迪中控安卓系统下载,畅享智能... 你有没有发现,现在汽车的中控系统越来越智能了?尤其是奥迪这种豪华品牌,他们的中控系统简直就是科技与艺...