C++11并发与多线程笔记(9) async、future、packaged_task、promise

这篇具有很好参考价值的文章主要介绍了C++11并发与多线程笔记(9) async、future、packaged_task、promise。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

std::async: 是一个函数模板,用来启动一个异步任务,启动起来一个异步任务之后,它返回一个std::future对象,这个对象是个类模板。

什么叫“启动一个异步任务”?就是自动创建一个线程,并开始执行对应的线程入口函数,它返回一个std::future对象,这个std::future对象中就含有线程入口函数所返回的结果,我们可以通过调用future对象的成员函数get()来获取结果

future”将来的意思,也有人称呼std::future提供了一种访问异步操作结果的机制,就是说这个结果你可能没办法马上拿到,但是在不久的将来,这个线程执行完毕的时候,你就能够拿到结果了,所以,大家这么理解:future中保存着一个值,这个值是在将来的某个时刻能够拿到。

std::future对象的get()成员函数会等待线程执行结束并返回结果,拿不到结果它就会一直等待,感觉有点像join()。但是,它是可以获取结果的。

std::future对象的wait()成员函数,用于等待线程返回,本身并不返回结果,这个效果和 std::thread 的join()更像。

#include <iostream>
#include <future>
using namespace std;
class A {
public:
	int mythread(int mypar) {
		cout << mypar << endl;
		cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
		std::chrono::milliseconds dura(5000);//定义一个5秒的时间
	    std::this_thread::sleep_for(dura);//创建一个线程并开始执行,绑定关系
	    cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;//卡在这里等待mythread()执行完毕,拿到结果
		return mypar;
	}
};
 
int mythread() {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);//定义一个5秒的时间
	std::this_thread::sleep_for(dura);//创建一个线程并开始执行,绑定关系
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;//卡在这里等待mythread()执行完毕,拿到结果
	return 5;
}
 
 
int main() {
	A a;
	int tmep = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result1 = std::async(mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; //卡在这里等待mythread()执行完毕,拿到结果,只能使用一次
	
	//类成员函数
	std::future<int> result2 = std::async(&A::mythread, &a, temp); //第二个参数是对象引用才能保证线程里执行的是同一个对象
	cout << result2.get() << endl;
   //或者result2.wait(); //等待线程返回,本身不返回结果
	cout << "good luck" << endl;
	return 0;
}

我们通过向std::async()传递一个参数,该参数是std::launch类型枚举类型),来达到一些特殊的目的:

  1. std::lunch::deferred:
    (defer推迟,延期)表示线程入口函数的调用会被延迟,一直到std::future的wait()或者get()函数被调用时(由主线程调用)才会执行;如果wait()或者get()没有被调用,则不会执行
    重点实际上根本就没有创建新线程。std::launch::deferred意思时延迟调用,并没有创建新线程,是在主线程中调用的线程入口函数。
#include <iostream>
#include <future>
using namespace std;
class A {
public:
	int mythread(int mypar) {
		cout << mypar << endl;
		cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
		std::chrono::milliseconds dura(5000);
		std::this_thread::sleep_for(dura);
		cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
		return mypar;
	}
};

int main() {
	A a;
	int temp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	cout << "continue........" << endl;
	std::future<int> result1 = std::async(std::launch::deferred, &A::mythread, &a, temp);
	cout << result1.get() << endl;
	//或者result2.wait();
	cout << "I love China!" << endl;
	return 0;
}

C++11并发与多线程笔记(9) async、future、packaged_task、promise,C++11并发与多线程笔记,c++,笔记,算法

  1. std::launch::async,在调用async函数的时候就开始创建新线程,不添加标记,默认用的就是默认值是 std::launch::async | std::launch::deferred 标记。
int main() {
	A a;
	int temp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	cout << "continue........" << endl;
	std::future<int> result1 = std::async(std::launch::async, &A::mythread, &a, temp);//使用std::launch::async标记
	cout << result1.get() << endl;
	//或者result2.wait();
	cout << "I love China!" << endl;
	return 0;
}
  1. 同时使用std::launch::async和std::lunch::deferred标记,并不能在新线程中延迟调用。

2、std::packaged_task:打包任务,把任务包装起来

类模板,它的模板参数是各种可调用对象,通过packaged_task把各种可调用对象包装起来,方便将来作为线程入口函数来调用。

#include <thread>
#include <iostream>
#include <future>
using namespace std;
 
int mythread(int mypar) {
	cout << mypar << endl;
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}
 
int main() {
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	//我们把函数mythread通过packaged_task包装起来
    //参数是一个int,返回值类型是int
    //方法1
    std::packaged_task<int(int)> mypt(mythread);
	std::thread t1(std::ref(mypt), 1);//线程开始执行
	t1.join();//等待线程执行完毕
	std::future<int> result = mypt.get_future(); 
	//std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
	cout << result.get() << endl;
	return 0;
}

可调用对象可由函数换成lambda表达式

int main() {
    //方法2,用lambda表达式
    std::packaged_task<int(int)> mypt([](int mypar){
   	cout << mypar << endl;
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
    });
    std::thread t1(std::ref(mypt), 1);
	t1.join();
	std::future<int> result = mypt.get_future(); 
	//std::future对象里包含有线程入口函数的返回结果,这里result保存mythread返回的结果。
	cout << result.get() << endl;
	cout << "I love China!" << endl;
	return 0;
}

packaged_task包装起来的可调用对象还可以直接调用,从这个角度来讲,packaged_task对象也是一个可调用对象
lambda的直接调用

int main() {
    //方法2,用lambda表达式
    std::packaged_task<int(int)> mypt([](int mypar){
   	cout << mypar << endl;
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
    });
    //packaged_task包装起来的可调用对象还可以直接调用,所以从这个角度来讲,pakcaged_task对象,也是一个可调用对象;
    mypt(105);//直接调用,相当于函数调用
    std::future<int> result=mypt.get_future();
    cout<<result.get()<<endl; 
	return 0;
}

包装后存放容器里

vector<std::packaged<int(int)>> mytasks;
int main() {
    //方法2,用lambda表达式
    std::packaged_task<int(int)> mypt([](int mypar){
   	cout << mypar << endl;
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
    });
	mytasks.push_back(std::move(mypt));//入容器,进去用了移动语义,入进去之后mypt就为空
	std::packaged_task<int(int)>mypt2;
	auto iter=mytask.begin();
	mypt2=std::move(*iter);//移动语义
	mytasks.erase(iter);//删除第一个元素,迭代已经失效了,所以后续代码不可以再使用iter
    mypt2(105);//直接调用,相当于函数调用
    std::future<int> result=mypt2.get_future();
    cout<<result.get()<<endl; 
	return 0;
}

3、std::promise

类模板,我们能够在某个线程中给它赋值,然后我们可以在其他线程中,把这个值取出来

#include <thread>
#include <iostream>
#include <future>
using namespace std;
 
void mythread(std::promise<int> &tmpp, int clac) {
	//做一系列复杂的操作
	clac++;
	clac *=10;
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	int result = clac;//保存结果
	tmp.set_value(result); //结果保存到了tmp这个对象中
	return;
}
 
int main() {
	std::promise<int> myprom;
	std::thread t1(mythread, std::ref(myprom), 180);
	t1.join(); //在这里线程已经执行完了
	std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值
	auto result = fu1.get();//get只能调用1次
	cout << "result = " << result << endl;
	cout<<"I love China!"<<endl;
}

总结:通过promise保存一个值,在将来某个时刻我们通过把一个future绑定到这个promise上,来得到绑定的值

使用两个子进程

#include <thread>
#include <iostream>
#include <future>
using namespace std;
 
void mythread(std::promise<int> &tmpp, int clac) {
	//做一系列复杂的操作
	clac++;
	clac *=10;
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	int result = clac;//保存结果
	tmp.set_value(result); //结果保存到了tmp这个对象中
	return;
}
 
void mythread2(std::future<int> &tmpf){
	auto result=tmpf.get();
	cout<<"mythread2.result"<<result<<endl;
	return;
}
 
int main() {
	std::promise<int> myprom;
	std::thread t1(mythread, std::ref(myprom), 180);
	t1.join(); //在这里线程已经执行完了
	std::future<int> fu1 = myprom.get_future(); //promise和future绑定,用于获取线程返回值
	
	std::thread t2(mythread2,std::ref(ful));
	t2.join();//等mythread2线程执行完毕
	
	cout<<"I love China!"<<endl;
}

总结:第一个线程1(t1) 计算了一个结果,结果通过future对象给到第二个线程2(t2)。

注意:使用thread时,必须 join() 或者 detach() 否则程序会报异常

3、小结

我们学习这些东西的目的并不是,要把他们都用到实际开发中。
相反,如果我们能够用最少的东西写出一个稳定的,高效的多线程程序,更值得赞赏。
我们为了成长必须阅读一些高手写的代码,从而实现自己代码的积累;文章来源地址https://www.toymoban.com/news/detail-662720.html

到了这里,关于C++11并发与多线程笔记(9) async、future、packaged_task、promise的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++11并发与多线程笔记(6) unique_lock(类模板)

    unique_lock 是一个类模板。 unique_lock 比 lock_guard 灵活很多 ( 多出来很多用法 ),效率差一点,内存占用多一些。 使用: unique_lockmutex myUniLock(myMutex); std::adopt_lock:标记作用,表示这个互斥量已经被lock()(方便记忆:已经被lock()收养了,不需要再次lock() ),即 不需要在构造函

    2024年02月12日
    浏览(42)
  • C++11并发与多线程笔记(3)线程传参详解,detach()大坑,成员函数做线程函数

    在使用detach时,不推荐引用传递,指针传递肯定有问题 在创建线程的同时构造临时对象的方法传递参数是可行的 如果传递int这种基本数据类型,推荐使用 值传递 ,不要用引用 如果传递 类对象 ,避免使用隐式类型转换,全部都是创建线程这一行就创建出 临时对象 ,然后在

    2024年02月12日
    浏览(40)
  • C++11并发与多线程笔记(7) 单例设计模式共享数据分析、解决,call_once

    程序灵活,维护起来可能方便,用设计模式理念写出来的代码很晦涩,但是别人接管、阅读代码都会很痛苦 老外应付特别大的项目时,把项目的开发经验、模块划分经验,总结整理成设计模式 中国零几年设计模式刚开始火时,总喜欢拿一个设计模式往上套,导致一个小小的

    2024年02月12日
    浏览(40)
  • C++ 并发编程 | future与async

    async 函数接受两种不同的启动策略,这些策略在 std::launch 枚举中定义,如下: std::launch::defered :这种策略意味着任务将在调用 future::get() 或 future::wait 函数时延迟执行,也就是任务将在需要结果时 同步 执行 std::launch::async :任务在单独一个线程上 异步 执行 默认情况下 asy

    2024年01月24日
    浏览(42)
  • 【C++11】future和async等

    C++11的future和async等 std::async 和 std::future 是 C++11 引入的标准库功能,用于实现异步编程,使得在多线程环境中更容易处理并行任务。它们可以帮助你在不同线程中执行函数,并且能够方便地获取函数的结果。 在之前使用线程的时候,我们没有办法很好的获取到线程所执

    2024年02月11日
    浏览(43)
  • 分布式集群与多线程高并发

      后台数据的处理语言有很多,Java 是对前端采集的数据的一种比较常见的开发语言。互联网移动客户端的用户量特别大,大量的数据处理需求应运而生。可移动嵌入式设备的表现形式   很多,如 PC 端,手机移动端,智能手表,Google  眼镜等。Server2client 的互联网开发模式比

    2024年02月08日
    浏览(46)
  • Python中的并发编程:多线程与多进程的比较【第124篇—多线程与多进程的比较】

    在Python编程领域中,处理并发任务是提高程序性能的关键之一。本文将探讨Python中两种常见的并发编程方式:多线程和多进程,并比较它们的优劣之处。通过代码实例和详细的解析,我们将深入了解这两种方法的适用场景和潜在问题。 多线程是一种轻量级的并发处理方式,适

    2024年03月14日
    浏览(98)
  • 再见了Future,图解JDK21虚拟线程的结构化并发

    Java为我们提供了许多启动线程和管理线程的方法。在本文中,我们将介绍一些在Java中进行并发编程的选项。我们将介绍 结构化并发 的概念,然后讨论 Java 21 中一组预览类——它使将任务拆分为子任务、收集结果并对其进行操作变得非常容易,而且不会不小心留下任何挂起的

    2024年02月05日
    浏览(57)
  • 【linux 多线程并发】多线程模型下的信号通信处理,与多进程处理的比较,属于相同进程的线程信号分发机制

    ​ 专栏内容 : 参天引擎内核架构 本专栏一起来聊聊参天引擎内核架构,以及如何实现多机的数据库节点的多读多写,与传统主备,MPP的区别,技术难点的分析,数据元数据同步,多主节点的情况下对故障容灾的支持。 手写数据库toadb 本专栏主要介绍如何从零开发,开发的

    2024年01月17日
    浏览(46)
  • 【Java基础教程】(四十二)多线程篇 · 上:多进程与多线程、并发与并行的关系,多线程的实现方式、线程流转状态、常用操作方法解析~

    理解进程与线程的区别; 掌握Java 中多线程的两种实现方式及区别; 掌握线程的基本操作方法; 进程是程序的一次动态执行过程,它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程 。多进程操作系统能同时运行多

    2024年02月16日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包