前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >UNIX(多线程):12---async、future、packaged_task、promise

UNIX(多线程):12---async、future、packaged_task、promise

作者头像
用户3479834
发布2021-02-03 14:58:44
发布2021-02-03 14:58:44
45300
代码可运行
举报
文章被收录于专栏:游戏开发司机游戏开发司机
运行总次数:0
代码可运行

std::async、std::future创建后台任务并返回值

  • 需求:希望线程返回一个结果.
  • std::async是个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,他返回一个std::future对象,std::future是个类模板。
    • 这个std::future对象里边就含有线程入口函数所返回的结果(线程返回的结果),我们可以通过调用future对象的成员函数get()来获取结果。
    • "future": 将来的意思,有人也称呼std::future提供了一种访问异步操作结果的机制,就是说这个结果你可能没有办法马上拿到,但不久的将来,
    • 什么叫“启动一个异步任务”,就是自动创建一个线程并开始执行对应的线程入口函数,它返回一个std::future对象。
  • 在线程执行完毕的时候,你就能够拿到结果了,所以大家就这么理解:这个future(对象)里会保存一个值,在将来的某个时刻能够拿到。
  • 下列程序通过std::future对象的get()成员函数等待线程执行结果并返回结果.
  • 这个get()函数很牛,不拿到将来的返回值 誓不罢休,不拿到值我就卡在这里等待拿值。
  • get()只能调用一次,不能调用多次。
  • wait(),等待线程返回,本身并不返回结果,类似于join()。
代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
int mythread() //线程入口函数
{
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
std::chrono::milliseconds dura(5000);  //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
return 100;
}
int main()
{
cout << "MianThreadID = " <<  std::this_thread::get_id() << endl;  //打印主线程id
std::future<int> ret = std::async(mythread);  //创建线程并开始执行
//将future对象与async创建的线程绑定到一起,流程不卡在这里,继续运行
std::cout << ret.get() << std::endl;  //卡在这里等待mythread执行完毕,拿到结果
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}

【使用类成员函数作为线程入口函数】

代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
class A{
public:
int a_thread(int var) //线程入口函数
{
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
std::chrono::milliseconds dura(5000);  //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
return 100;
}
};
int main()
{
A a;
int tmp{666};
cout << "MianThreadID = " <<  std::this_thread::get_id() << endl;  //打印主线程id
std::future<int> ret = std::async(&A::a_thread, &a, tmp);  //第二个参数是一个对象引用,才能保证线程里使用的是同一个对象
std::cout << ret.get() << std::endl;  //卡在这里等待mythread执行完毕,拿到结果
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}
  • 如果上面代码中不使用get函数,结果如下
  • 我们通过额外向std::async()传递一个参数,该参数类型是std::lunnch类型(枚举类型)来达到一些特殊的目的。

【std::launch::deferred】

  • a) std::launch::deferred: 表示线程入口函数调用被延迟到std::future的wait()或者get()函数调用时才执行。
    • 那如果wait()或者get()没有被调用,那么线程会执行吗?没执行,实际上,线程根本就没创建。
    • std::launch::deferred: 延迟调用,并且没有创建新线程,是在主线程中调用的线程入口函数。

【std::launch::async】

  • std::launch::async,在调用async函数的时候就开始创建线程,即立即执行。
    • async()函数,默认用的就是std::launch::async标记。

std::packaged_task(包装各种可调用对象),配合thread类、future类使用

  • 是个类模板,它的模板参数是 各种可调用对象;通过std::packaged_task来把各种可调用对象包装起来,方便将来作为线程入口函数来调用。
代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
int mythread(int var) //线程入口函数
{
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
std::chrono::milliseconds dura(5000);  //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
return 100;
}
int main()
{
cout << "MianThreadID = " <<  std::this_thread::get_id() << endl;  //打印主线程id
std::packaged_task<int(int)> myTask(mythread); //将函数mythread包装起来
int tmp{ 233 };
std::thread objThread(std::ref(myTask), tmp);  //线程直接开始执行,第二个参数作为线程入口函数的参数
objThread.join();   //等待线程执行完毕
std::future<int> ret = myTask.get_future(); //future包含有线程入口函数的返回结果,这里保存mythread的返回结果
cout << ret.get() << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}

【使用lambda表达式】

代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
int main()
{
cout << "MianThreadID = " <<  std::this_thread::get_id() << endl;  //打印主线程id
std::packaged_task<int(int)> myTask([](int var) {
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
std::chrono::milliseconds dura(5000);  //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
return 100;
}); //将lambda表达式包装起来
int tmp{ 233 };
std::thread objThread(std::ref(myTask), tmp);  //线程直接开始执行,第二个参数作为线程入口函数的参数
objThread.join();   //等待线程执行完毕
std::future<int> ret = myTask.get_future(); //future包含有线程入口函数的返回结果,这里保存lambda表达式的返回结果
cout << ret.get() << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}

【packaged_task作为可调用对象直接调用】

  • packaged_task包装起来的可调用对象还可以直接调用,所以从这个角度来讲,packaged_task对象,也是一个可调用对象。
代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
int main()
{
cout << "MianThreadID = " <<  std::this_thread::get_id() << endl;  //打印主线程id
std::packaged_task<int(int)> myTask([](int var) {
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
std::chrono::milliseconds dura(5000);  //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
return 100;
}); //将lambda表达式包装起来
int tmp{ 233 };
myTask(tmp);    //直接调用,相当于直接调用
std::future<int> ret = myTask.get_future(); //future包含有线程入口函数的返回结果,这里保存返回结果
cout << ret.get() << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}

【结合容器和移动语义使用】

代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
std::vector<std::packaged_task< int(int)>>  TaskVector;
int main()
{
cout << "MianThreadID = " <<  std::this_thread::get_id() << endl;  //打印主线程id
std::packaged_task<int(int)> myTask([](int var) {
cout << var << endl;
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
std::chrono::milliseconds dura(5000);  //休息5秒
std::this_thread::sleep_for(dura);
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
return 100;
}); //将lambda表达式包装起来
TaskVector.push_back(std::move(myTask));  //加入至容器,使用了移动语义,进去后myTask就为空
//...
//取出来调用
std::packaged_task< int(int)>  myTask2;
auto iter = TaskVector.begin();
myTask2 = std::move(*iter);
TaskVector.erase(iter);   //删除第一个元素,之后迭代器就失效了,后面不可以使用iter了
myTask2(72);    //直接调用,相当于直接调用
std::future<int> ret = myTask2.get_future(); //future包含有线程入口函数的返回结果,这里保存返回结果
cout << ret.get() << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}

std::promise

  • 我们能够再某个线程中给它赋值,然后我们可以再其他线程中,把这个值取出来用。
代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
void mythread(std::promise<int>& tmp, int clc)
{
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
//做一系列复杂运算
clc++;
clc *= 233;
//其他运算,比如花费了5秒钟
std::chrono::milliseconds dura(5000);  //休息5秒
std::this_thread::sleep_for(dura);
//计算出结果
int ret = clc;  //保存结果
tmp.set_value(ret); //结果保存到tmp中
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
}
int main()
{
cout << "MianThreadID = " <<  std::this_thread::get_id() << endl;  //打印主线程id
std::promise<int> var_pro;  //创建一个promise对象,用来保存int类型的值
std::thread objThread(mythread, std::ref(var_pro), 10);
//获取结果
objThread.join();
std::future<int>  t1 = var_pro.get_future(); //promise和线程绑定,用于获取线程返回值
auto val1 = t1.get();
cout << val1 << endl;
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}

【线程1中返回值,线程2调用(promise + future 起连接作用),实现两个线程之间数据传递】

代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
#include <future>
using namespace std;
void mythread(std::promise<int>& tmp, int clc)
{
cout << "mythread start" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
//做一系列复杂运算
clc++;
clc *= 233;
//其他运算,比如花费了5秒钟
std::chrono::milliseconds dura(5000);  //休息5秒
std::this_thread::sleep_for(dura);
//计算出结果
int ret = clc;  //保存结果
tmp.set_value(ret); //结果保存到tmp中
cout << "mythread end" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
return;
}
void mythread2(std::future<int>& tmp)
{
cout << "mythread2 start" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
auto val = tmp.get();
cout <<"mythread2 val = " <<  val << endl;
cout << "mythread2 end" << "ThreadId = " << std::this_thread::get_id() << endl;  //打印线程id
return;
}
int main()
{
cout << "MianThreadID = " <<  std::this_thread::get_id() << endl;  //打印主线程id
std::promise<int> var_pro;  //创建一个promise对象,用来保存int类型的值
std::thread objThread(mythread, std::ref(var_pro), 10);
//获取结果
objThread.join();
std::future<int>  t1 = var_pro.get_future(); //promise和线程绑定,用于获取线程返回值
std::thread objThread2(mythread2, std::ref(t1));
//获取结果
objThread2.join();        //等线程2执行完毕
//主线程执行
std::cout << "主线程结束" << std::endl;
return 0;
}

【小结】

  • 这些东西到底怎么用,什么时候用?
    • 我们学习这些东西的目的,并不是要把他们都用在咱们自己的实际开发中。
    • 相反,如果我们能够用最少的东西能够写出一个稳定、高效的多线程程序,更值得赞赏。
    • 我们为了成长,必须要阅读一些高手写的代码,从而快速实现自己代码的积累,我们的技术就会有一个大幅度的提升。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • std::async、std::future创建后台任务并返回值
  • std::packaged_task(包装各种可调用对象),配合thread类、future类使用
  • std::promise
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档