C++异步操作future和aysnc与function和bind

目录
  • 异步操作
    • std::future和std::aysnc 介绍
    • std::future和std::aysnc的使用Demo
    • std::packaged_task 介绍
    • std::packaged_task的使用Demo
    • std::promise 的介绍
    • std::promise的使用Demo
  • function和bind
    • function的用法
    • bind的用法

异步操作

C++11为异步操作提供了4个接口

  • std::future : 异步指向某个任务,然后通过future特性去获取任务函数的返回结果。
  • std::aysnc: 异步运行某个任务函数。
  • std::packaged_task :将任务和feature绑定在一起的模板,是一种封装对任务的封装。
  • std::promise:承诺

std::future和std::aysnc 介绍

std::future期待一个函数的返回值,从一个异步调用的角度来说,future更像是执行函数的返回值,C++标准库使用std::future为一次性事件建模,如果一个事件需要等待特定的一次性事件,那么这线程可以获取个future对象来代表这个事件。

异步调用往往不知道何时返回,但是如果异步调用的过程需要同步,或者说后一个异步调用需要使用前一个异步调用的结果。这个时候就要用到future。也就是说,可选择同步,也可选择异步。

future的表现为期望,当前线程持有future时,期望从future获取到想要的结果和返回,可以把future当做异步函数的返回值。

线程可以周期性的在这个future上等待一小段时间,检查future是否已经ready,如果没有,该线程可以先去做另一个任务,一旦future就绪,该future就无法复位(无法再次使用这个future等待这个事件),所以future代表的是一次性事件。

在库的头文件中声明了两种future,唯一future(std::future)和共享future(std::shared_future)这两个是参照std::unique_ptr和std::shared_ptr设立的,前者的实例是仅有的一个指向其关联事件的实例,而后者可以有多个实例指向同一个关联事件,当事件就绪时,所有指向同一事件的std::shared_future实例会变成就绪。

跟thread类似,async允许你通过将额外的参数添加到调用中,来将附加参数传递给函数。如果传入的函数指针是某个类的成员函数,则还需要将类对象指针传入(直接传入,传入指针,或者是std::ref封装)。

默认情况下,std::async是否启动一个新线程,或者在等待future时,任务是否同步运行都取决于你给的参数。这个参数为std::launch类型,async运行某个任务函数,至于异步运行还是同步运行,由这个参数决定

默认选项参数被设置为std::launch::any。如果函数被延迟运行可能永远都不会运行,因为很有可能对应的future没有调用get。

enum class launch{
	async,deferred,sync=deferred,any=async|deferred
};
  • std::launch::async,表明函数会在创建的新线程上运行。
  • std::launch::defered表明该函数会被延迟调用,直到在future上调用get()或者wait()为止。
  • std::launch::sync = std::launch::defered,表明该函数会被延迟调用
  • std::launch::any = std::launch::defered | std::launch::async,表明该函数会被延迟调用,调用时在新线程上运行。

std::future和std::aysnc的使用Demo

这里我们future了两个函数,第一个函数设置为异步,那么在第20行之后,就会创建一个新线程并运行,而不必等待result.get()。第二个函数没有设置参数,那么默认是延迟调用,只有在result2.get()时,才会创建一个新线程并运行。

#include <iostream>
#include <future>
#include <thread>
using namespace std;
int find_result_to_add() {
	//std::this_thread::sleep_for(std::chrono::seconds(2)); // 用来测试异步延迟的影响
	std::cout << "find_result_to_add" << std::endl;
	return 1 + 1;
}
int find_result_to_add2(int a, int b) {
	//std::this_thread::sleep_for(std::chrono::seconds(5)); // 用来测试异步延迟的影响
	return a + b;
}
void do_other_things() {
	std::cout << "do_other_things" << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(5));
}
int main() {
	//async异步
	std::future<int> result = std::async(std::launch::async,find_result_to_add);
	//std::future<decltype (find_result_to_add())> result = std::async(find_result_to_add);
	//auto result = std::async(find_result_to_add); // 推荐的写法用aoto 

	do_other_things();

	std::cout << "result: " << result.get() << std::endl; // 延迟是否有影响?

	//std::future<decltype (find_result_to_add2(0, 0))> result2 = std::async(find_result_to_add2, 10, 20);
	//不写默认any
	auto result2=std::async(find_result_to_add2, 10, 20);

	std::cout << "result2: " << result2.get() << std::endl; // 延迟是否有影响?

	std::cout << "main finish" << endl;
	return 0;
}

std::packaged_task 介绍

如果说std::async和std::feature还是分开看的关系的话,那么std::packaged_task就是将任务和feature绑定在一起的模板,是一种封装对任务的封装。

The class template std::packaged_task wraps any Callable target (function, lambda expression, bind expression, or another function object) so that it can be invoked asynchronously. Its return value or exception thrown is stored in a shared state which can be accessed through std::future objects.

可以通过std::packaged_task对象获取任务相关联的feature,调用get_future()方法可以获得std::packaged_task对象绑定的函数的返回值类型的future。std::packaged_task的模板参数是函数签名。( 例如int add(int a, intb)的函数签名就是int(int, int) )

std::packaged_task的使用Demo

#include <iostream>
#include <future>
using namespace std;
int add(int a, int b, int c) {
	std::cout << "call add\n";
	return a + b + c;
}
void do_other_things() {
	std::cout << "do_other_things" << std::endl;
}
int main() {
	std::packaged_task<int(int, int, int)> task(add); // 封装任务
	do_other_things();
	std::future<int> result = task.get_future();
	task(1, 1, 2); //必须要让任务执行,否则在get()获取future的值时会一直阻塞
	std::cout << "result:" << result.get() << std::endl;
	return 0;
}

std::promise 的介绍

std::promise提供了一种设置值的方式,它可以在这之后通过相关联的std::future对象进行读取。换种说法,之前已经说过std::future可以读取一个异步函数的返回值了,那么这个std::promise就提供一种方式手动让future就绪

出在promise创建好的时候future也已经创建好了,线程在创建promise的同时会获得一个future,然后将promise传递给设置他的线程,当前线程则持有future,以便随时检查是否可以取值。

promise是一个承诺,当线程创建了promise对象后,这个promise对象向线程承诺他必定会被人设置一个值,和promise相关联的future就是获取其返回的手段。

std::promise的使用Demo

#include <future>
#include <string>
#include <thread>
#include <iostream>
using namespace std;
void print(std::promise<std::string>& p) {
	p.set_value("There is the result whitch you want.");
}
void do_some_other_things() {
	std::cout << "Hello World" << std::endl;
}
int main() {
	std::promise<std::string> promise;
	std::future<std::string> result = promise.get_future();

	std::thread th(print, std::ref(promise));

	do_some_other_things();
	std::cout << result.get() << std::endl;

	th.join();
	return 0;
}

function和bind

在设计回调函数的时候,无可避免地会接触到可回调对象。在C++11中,提供了std::function和std::bind两个方法来对可回调对象进行统一和封装。(回调函数就是一个被作为参数传递的函数)

C++语言中有几种可调用对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。和其他对象一样,可调用对象也有类型。例如,每个lambda有它自己唯一的(未命名)类类型;函数及函数指针的类型则由其返回值类型和实参类型决定。

function的用法

头文件:#include <functional>

  • 保存普通函数
//保存普通函数
void func1(int a) {
	cout << a << endl;
}
//1. 保存普通函数
std::function<void(int a)> func1_;
func1_ = func1;
func1_(2); //2
  • 保存lambda表达式
//2. 保存lambda表达式
std::function<void()> func2_ = []() {
	cout << "hello lambda" << endl;
};
func2_(); //hello world
  • 保存成员函数
//保存成员函数
class A {
	public:
		A(string name) : name_(name) {}
		void func3(int i) const {
			cout <<name_ << ", " << i << endl;
		}
	private:
		string name_;
};

//3 保存成员函数
	std::function<void(const A&,int)> func3_ = &A::func3;
	A a("wxf");
	func3_(a, 20);

完整代码:

#include <iostream>
#include <functional>
using namespace std;
//保存普通函数
void func1(int a) {
	cout << a << endl;
}
//保存成员函数
class A {
	public:
		A(string name) : name_(name) {}
		void func3(int i) const {
			cout <<name_ << ", " << i << endl;
		}
	private:
		string name_;
};
int main() {
	cout << "main1 -----------------" << endl;
	//1. 保存普通函数
	std::function<void(int a)> func1_;
	func1_ = func1;
	func1_(2); //2

	cout << "\n\nmain2 -----------------" << endl;
	//2. 保存lambda表达式
	std::function<void()> func2_ = []() {
		cout << "hello lambda" << endl;
	};
	func2_(); //hello world

	cout << "\n\nmain3 -----------------" << endl;
	//3 保存成员函数
	std::function<void(const A&,int)> func3_ = &A::func3;
	A a("wxf");
	func3_(a, 20);
	return 0;
}
main1 -----------------
2
main2 -----------------
hello lambda
main3 -----------------
wxf, 20

bind的用法

可将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。调用bind的一般形式:auto newCallable = bind(callable, arg_list);

其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。即,当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。

arg_list中的参数可能包含形如placeholders::_n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置:placeholders::_1为newCallable的第一个参数,placeholders::_2为第二个参数,以此类推。

可能看描述还不是很懂,下面来看看代码:

#include <iostream>
#include <functional>
using namespace std;

void fun_1(int x,int y,int z) {
	cout<<"fun_1 print unchanged: x=" <<x<<",y="<< y << ",z=" <<z<<endl;
}
void fun_2(int &a,int &b) {
	a++;
	b++;
	cout<<"fun_2 print Increment: a=" <<a<<",b="<<b<<endl;
}
class A {
	public:
		// 重载fun_3,主要bind的时候需要
		// std::bind((void(A::*)(int, int))&A::fun_3
		void fun_3(int k,int m) {
			cout << "fun_3 a = " << a << cout <<"\t print unchanged: k="<<k<<",m="<<m<<endl;
		}
		// std::bind((void(A::*)(string))&A::fun_3
		void fun_3(string str) {
			cout<<"fun_3 print: str="<<str<<endl;
		}
		int a;
};
int main() {
	//f1的类型为 function<void(int, int, int)>
	cout << "\n\nstd::bind(fun_1, 1, 2, 3) -----------------\n";
	auto f1 = std::bind(fun_1, 1, 2, 3); //表示绑定函数 fun 的第一,二,三个参数值为:1 2 3
	f1(); //print: x=1,y=2,z=3

	cout << "\n\nstd::bind(fun_1, 10, 20, 30) -----------------\n";
	auto f1_1 = std::bind(fun_1, 10, 20, 30); //表示绑定函数 fun 的第一,二,三个参数值为: 1 2 3
	f1_1();

	cout << "\n\nstd::bind(fun_1, placeholders::_1,placeholders::_2, 3) -----------------\n";
	auto f2 = std::bind(fun_1, placeholders::_1, placeholders::_2, 3);
	//表示绑定函数 fun_1的第三个参数为 3,而fun_1的第一,二个参数分别由调用 f2 的第一,二个参数指定
	f2(1,2);//print: x=1,y=2,z=3
	f2(10,21,30); // 传入30也没有用

	cout << "\n\nstd::bind(fun_1,placeholders::_2,placeholders::_1,3) -----------------\n";
	auto f3 = std::bind(fun_1,placeholders::_2,placeholders::_1,3);
	//表示绑定函数 fun_1 的第三个参数为 3,而fun_1的第一,二个参数分别由调用 f3 的第二,一个参数指定
	//注意: f2 和 f3 的区别。
	f3(1,2);//print: x=2,y=1,z=3

	cout << "\n\nstd::bind(fun_2, placeholders::_1, n) -----------------\n";
	int m = 2;
	int n = 3;
	////表示绑定fun_2的第一个参数为n, fun_2的第二个参数由调用f4的第一个参数(_1)指定。
	auto f4 = std::bind(fun_2, placeholders::_1, n); //func_2(3,<f4_1>)
	f4(m); //print: m=3,n=4
	cout<<"m="<<m<<endl;//m=3 说明:bind对于不事先绑定的参数,通过std::placeholders传递的参数是通过引用传递的,如m
	cout<<"n="<<n<<endl;//n=3 说明:bind对于预先绑定的函数参数是通过值传递的,如n

	cout << "\n\nstd::bind(&A::fun_3,&a1,40,50) -----------------\n";
	A a;
	a.a = 10;
	//f5的类型为 function<void(int, int)>
	auto f5 = std::bind((void(A::*)(int, int))A::fun_3, &a, 40, 50);
	f5(10,20);//参数以及写死,传参没用 

	cout << "\n\nstd::bind(&A::fun_3, &a2,placeholders::_1,placeholders::_2) -----------------\n";
	A a2;
	a2.a = 20;
	//f5的类型为 function<void(int, int)>
	auto f6 = std::bind((void(A::*)(int, int))&A::fun_3,&a2,placeholders::_1,placeholders::_2); //使用auto关键字
	f6(10,20);//调用a.fun_3(10,20),print: k=10,m=20

	cout << "\n\nstd::bind(&A::fun_3,a3,std::placeholders::_1,std::placeholders::_2) -----------------\n";
	std::function<void(int,int)> fc = std::bind((void(A::*)(int,int))&A::fun_3, &a,std::placeholders::_1,std::placeholders::_2);
	fc(10,20); //调用a.fun_3(10,20) print: k=10,m=20
	fc = std::bind((void(A::*)(int, int))&A::fun_3,&a2,std::placeholders::_1,std::placeholders::_2);

	cout << "\n\nstd::bind(&A::fun_3,&a1,std::placeholders::_1) -----------------\n";
	auto f_str = std::bind((void(A::*)(string))&A::fun_3,a,std::placeholders::_1);
	f_str("wxf");

	return 0;
}

到此这篇关于C++异步操作future和aysnc与function和bind的文章就介绍到这了,更多相关C++异步操作内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • UE4 Unlua 调用异步蓝图节点AIMoveTo函数示例详解

    目录 引言 源码分析 Unlua代码实现 引言 异步蓝图节点:在蓝图节点的右上角有时钟图标. 注意:异步节点可以在EventGraph/Macros中使用,但是无法在蓝图函数中使用. AIMoveTo节点:实现AI自主寻路,且能异步回调执行成功或失败的委托函数,且返回移动结果枚举值. 源码分析 AIMoveTo 蓝图节点对应C++的基类为 UK2Node_AIMoveToUK2Node_AIMoveTo 继承至异步Task节点基类 UK2Node_BaseAsyncTask,并在构造函数中完成了

  • C/C++ Qt TreeWidget 嵌套节点操作使用

    目录 简单的节点遍历 初始化树形节点 单击双击节点反馈 添加 父节点/子节点 删除选中节点 修改指定节点名称 枚举所有节点元素 枚举选中节点元素 获取选中子节点的父节点 在上一篇博文<C/C++ Qt TreeWidget 单层树形组件应用>中给大家演示了如何使用TreeWidget组件创建单层树形结构,并给这个树形组件增加了右键菜单功能,接下来将继续延申树形组件的使用,并实现对树形框多节点的各种操作. 常用树形框节点间的操作方法如下: 节点遍历 初始化节点 单击双击节点 添加根节点 添加子节

  • C++ 线程(串行 并行 同步 异步)详解

    C++  线程(串行 并行 同步 异步)详解 看了很多关于这类的文章,一直没有总结.不总结的话就会一直糊里糊涂,以下描述都是自己理解的非官方语言,不一定严谨,可当作参考. 首先,进程可理解成一个可执行文件的执行过程.在ios app上的话我们可以理解为我们的app的.ipa文件执行过程也即app运行过程.杀掉app进程就杀掉了这个app在系统里运行所占的内存. 线程:线程是进程的最小单位.一个进程里至少有一个主线程.就是那个main thread.非常简单的app可能只需要一个主线程即UI线程.

  • C++ boost::asio编程-异步TCP详解及实例代码

    C++ boost::asio编程-异步TCP 大家好,我是异步方式 和同步方式不同,我从来不花时间去等那些龟速的IO操作,我只是向系统说一声要做什么,然后就可以做其它事去了.如果系统完成了操作, 系统就会通过我之前给它的回调对象来通知我. 在ASIO库中,异步方式的函数或方法名称前面都有"async_ " 前缀,函数参数里会要求放一个回调函数(或仿函数).异步操作执行 后不管有没有完成都会立即返回,这时可以做一些其它事,直到回调函数(或仿函数)被调用,说明异步操作已经完成. 在ASI

  • C++链表节点的添加和删除介绍

    目录 前言 1. 节点的创建 2. 链表的定义 3. 创建节点 4. 节点的插入 4.1 头插法 4.2 尾插法 4.3 插入中间节点 总结 前言 链表是一种动态的数据结构,因为在创建链表时,不需要知道链表的长度,只需要对指针进行操作. 1. 节点的创建 链表的节点包括两部分,分别是:数据域和(指向下一个节点的)指针域. struct Node { int data; struct Node* next; }; 2. 链表的定义 struct Node* createList() { //创建一

  • C++异步操作future和aysnc与function和bind

    目录 异步操作 std::future和std::aysnc 介绍 std::future和std::aysnc的使用Demo std::packaged_task 介绍 std::packaged_task的使用Demo std::promise 的介绍 std::promise的使用Demo function和bind function的用法 bind的用法 异步操作 C++11为异步操作提供了4个接口 std::future : 异步指向某个任务,然后通过future特性去获取任务函数的返

  • Function.prototype.bind用法示例

    复制代码 代码如下: //ECMAScript 5 Function.prototype.bind函数兼容处理 (function(){ if ( !Function.prototype.bind ) { //function(){}.bind Function.prototype.bind = function ( o, /*参数列表*/ ) { var self = this, boundArgs = Array.prototype.slice.call(arguments, 0); ret

  • 理解javascript中的Function.prototype.bind的方法

    在初学Javascript时,我们也许不需要担心函数绑定的问题,但是当我们需要在另一个函数中保持上下文对象this时,就会遇到相应的问题了,我见过很多人处理这种问题都是先将this赋值给一个变量(比如self._this.that等),尤其是var that = this是我见的最多的,这样当你改变环境之后就可以使用它.这些都是可以的,但是还有一种更好的.更专有的方法,那就是使用Function.prototype.bind,下面进行详尽的讲解. 第一部分:需要解决的问题 首先看下面的代码 va

  • Javascript Function.prototype.bind详细分析

      Function.prototype.bind分析 bind()方法会创建一个新的函数,成为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入的第一个参数作为this,传入bind()方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调取原函数. 实际使用中我们经常会碰到这样的问题: var name = "pig"; function Person(name){ this.name = name; this.getName = function

  • javascript中的Function.prototye.bind

    函数绑定(Function binding)很有可能是你在开始使用JavaScript时最少关注的一点,但是当你意识到你需要一个解决方案来解决如何在另一个函数中保持this上下文的时候,你真正需要的其实就是 Function.prototype.bind(),只是你有可能仍然没有意识到这点. 第一次遇到这个问题的时候,你可能倾向于将this设置到一个变量上,这样你可以在改变了上下文之后继续引用到它.很多人选择使用 self, _this 或者 context 作为变量名称(也有人使用 that)

  • 深入理解JS中的Function.prototype.bind()方法

    前言 对于函数绑定(Function binding)很有可能是大家在使用JavaScript时最少关注的一点,但是当你意识到你需要一个解决方案来解决如何在另一个函数中保持this上下文的时候,你真正需要的其实就是 Function.prototype.bind() ,只是你有可能仍然没有意识到这点. 第一次遇到这个问题的时候,你可能倾向于将this设置到一个变量上,这样你可以在改变了上下文之后继续引用到它. 一. bind的语法 bind() 方法的主要作用就是将函数绑定至某个对象,bind(

  • C++中使用function和bind绑定类成员函数的方法详解

    定义一个普通的类 class Test1{ public: void fun(int val){ cout<<"hello world "<<val<<endl; } }; 开始第一个测试 int main(){ Test1 t; function<void(int)> pf = std::bind(&Test1::fun,t,2); pf(4); // return 0; } 输出的值是2,说明pf传进去的4并没有什么用,在bi

  • C++类重载函数的function和bind使用示例

    在没有C++11的std::function和std::bind之前,我们使用函数指针的方式是五花八门,结构很繁琐难懂.C++11中提供了std::function和std::bind统一了可调用对象的各种操作. 1.std::function简介 std::function首先是可调用对象,本质上生成了一个类(仿函数) 简单的使用如下代码 #include <unordered_map> #include <iostream> #include <functional>

  • C++11 学习笔记之std::function和bind绑定器

    std::function C++中的可调用对象虽然具有比较统一操作形式(除了类成员指针之外,都是后面加括号进行调用),但定义方法五花八门.为了统一泛化函数对象,函数指针,引用函数,成员函数的指针的各种操作,让我们可以按更统一的方式写出更加泛化的代码,C++11推出了std::function. std::function是可调用对象的包装器.它是一个类模板,可以容纳除了类成员(函数)指针之外的所有可调用对象.通过指定它的模板参数,它可以用统一的方式处理函数,函数对象,函数指针,并允许保存和延迟

  • Function.prototype.apply()与Function.prototype.call()小结

    老是忘掉这两个东东的用下,写下来做个记录吧. 他们作用是一模一样的,只是传入的参数不一样 apply apply接受两个参数,第一个制定了函数体内this对象的指向,第二个参数为一个带下标的集合(可遍历对象),apply方法把这个集合中的元素作为参数传递给被调用的函数: var func = function(a, c, c){ alert([a,b,c]); //[1,2,3] } func.apply(null, [1,2,3]); call call传入的参数不固定,和apply相同的是,

随机推荐