异步实现方式一:异步回调
2018-03-21 18:07
337 查看
异步回调的实现依赖于多线程或者多进程
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。
在面向对象的语言中,回调则是通过接口或抽象类来实现的,我们把实现这种接口的类成为回调类,回调类的对象成为回调对象。对于象C++这些兼容了过程特性的对象语言,不仅提供了回调对象、回调方法等特性,也能兼容过程语言的回调函数机制。Windows平台的消息机制也可以看作是回调的一种应用,我们通过系统提供的接口注册消息处理函数(即回调函数),从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的,我们可以认为它也是回调函数的一个特例。
简单来讲就是:把需要访问其他函数数据的函数(即callback函数)作为实参传递,callback函数处理回原逻辑
可以看出,函数的定义和函数指针的定义非常类似。一般为了简化函数指针类型的变量定义,提高程序的可读性,我们需要把函数指针类型自定义一下。
回调函数可以象普通函数一样被程序调用,但是只有它被当作参数传递给被调函数时才能称作回调函数。同步回调:#include <iostream>
typedef void (*Fun)(int);//定义一个函数指针类型
Fun p = NULL;//用Fun定义一个变量p,它指向一个返回值为空参数为int的函数
void caller(Fun pCallback)
{
p = pCallback;
printf(" in function \n");
//达成某一条件后,通过名片(函数指针p),传回结果
int result = 1;
(*p)(result);
}
void callback(int a)//回调函数
{
std::cout << "callback result = " << a << std::endl;
}
int main(int argc, char* argv[])
{
caller(callback);
printf("out function \n");//同步的回调
getchar();
return 0;
}
指针p和callee()的类型不兼容,因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p,尽管两者有相同的返回值和参数列
异步回调理解版例子:
在A类中定义一个callback回调函数作为返回A类所处线程的接口,然后通过call函数去创建新的线程2并运行,线程2中调用b类的answer函数,answer函数把回调函数指针callback作为实参,当前call函数在创建完线程后是不阻塞的,可以继续运行dosomething函数,。 。。在线程2中的answer函数通过调用实参callback函数指针重新回到线程1。
/**
* 这是一个回调接口,里面定义的方法就是回调函数
*/
public interface CallBack {
/**
* 这是一个回调函数,用于回答者B知道答案后给提问者A回电话,并告知提问者A答案是什么
* 这个回电话的方式callBack是提问者A确定的,所以这个方法的实现类是A类
* 这个回电话的内容result是回答者B提供的,所以这个变量的值是在B类中确定的
*/
public void callBack(String result);
}
[java] view plain copy
/**
* 提问者A类
*/
public class A implements CallBack{
/**
* 提问者A是知道回答者B的联系方式的
* 这里以一个B类引用代表,并在构造方法中传入B实例对象
*/
private B b;
public A(B b){
this.b = b;
}
/**
* 提问者A向回答者B打电话提问题
* 这里以一个call方法表示,并把问题参数传入
*/
public void call(final String question){
/**
* 建立提问者A线程,与回答者B线程结合,构成一个异步的环境
*/
new Thread(new Runnable() {
@Override
public void run() {
/**
* B接电话,听问题
* 这里以调用回答者B的answer方法表示,传入回调方法类参数、问题参数,以表示谁打的电话,问啥了
* 因为A类实现了CallBack接口,所以A类就是回调方法类,回调方法类实现了回调方法
*/
b.answer(A.this, question);
}
}).start();
/**
* 提问者提完问,去干别事情
*/
doOtherThing();
}
public void doOtherThing(){
System.out.println("我是提问者A,我问完问题就去干别的事情了!");
}
/**
* 刚刚说到,这个回电话的方式callBack是提问者A确定的,所以这个方法的实现类是A类
* 所以这里实现回调方法,代表回复的方法是回电话,由回答者B调用
*/
@Override
public void callBack(String result) {
System.out.println("B调用A定义的回调函数:回答者B告诉提问者A,问题的答案是:"+ result);
}
}
[java] view plain copy
/**
* 回答者B类
*/
public class B {
/**
* 回答者B接电话,听问题 这里以调用回答者B的answer方法表示,传入回调方法类、问题参数,以表示谁打的电话,问啥了
*/
public void answer(CallBack callBack, String question) {
System.out.println("A调用B的接电话方法:我是回答者B,提问者A问的问题是:" + question);
/**
* 模拟回答者B先忙自己的事
*/
System.out.println("我是回答者B,我接完电话先去忙我自己的事!");
for (int i = 0; i < 100000; i++) {
}
String result = "2";
System.out.println("我是回答者B,我知道了答案是:" + result);
/**
* 调用回调函数,打电话告知A答案是什么
*/
callBack.callBack(result);
}
}
[java] view plain copy
/**
* 场景测试类
*/
public class test {
public static void main(String args[]){
/**
* 实例化回答者B
*/
B b = new B();
/**
* 实例化提问者A
*/
A a = new A(b);
/**
* A向B提问,开始
*/
a.call("1 + 1 = ?");
}
}
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。
在面向对象的语言中,回调则是通过接口或抽象类来实现的,我们把实现这种接口的类成为回调类,回调类的对象成为回调对象。对于象C++这些兼容了过程特性的对象语言,不仅提供了回调对象、回调方法等特性,也能兼容过程语言的回调函数机制。Windows平台的消息机制也可以看作是回调的一种应用,我们通过系统提供的接口注册消息处理函数(即回调函数),从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的,我们可以认为它也是回调函数的一个特例。
2 过程语言中的回调(C)
回调的应用常见场景:用户在断线重连时,login_user类需要调用mgr类中的函数B去获取登陆玩家的信息,那么函数B需要login_user类提供一个usid,login_user类则需要提供一个callback函数来接收mgr类中函数B所提供的信息,callback函数处理login_user类中的业务。简单来讲就是:把需要访问其他函数数据的函数(即callback函数)作为实参传递,callback函数处理回原逻辑
2.1 函数指针
回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此,要实现回调,必须首先定义函数指针,请看下面的例子:typedef void (*Fun)(int);//定义一个函数指针类型
Fun p = NULL;//用Fun定义一个变量p,它指向一个返回值为空参数为int的函数
void caller(Fun pCallback)
{
p = pCallback;
printf(" in function \n");
//达成某一条件后,通过名片(函数指针p),传回结果
int result = 1;
(*p)(result);
}
void callback(int a)//回调函数
{
std::cout << "callback result = " << a << std::endl;
}
int main(int argc, char* argv[])
{
caller(callback);
printf("out function \n");//同步的回调
getchar();
return 0;
}
2.2 参数传递规则
到目前为止,我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。将调用规范看成是函数类型的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。例如:异步回调理解版例子:
在A类中定义一个callback回调函数作为返回A类所处线程的接口,然后通过call函数去创建新的线程2并运行,线程2中调用b类的answer函数,answer函数把回调函数指针callback作为实参,当前call函数在创建完线程后是不阻塞的,可以继续运行dosomething函数,。 。。在线程2中的answer函数通过调用实参callback函数指针重新回到线程1。
/**
* 这是一个回调接口,里面定义的方法就是回调函数
*/
public interface CallBack {
/**
* 这是一个回调函数,用于回答者B知道答案后给提问者A回电话,并告知提问者A答案是什么
* 这个回电话的方式callBack是提问者A确定的,所以这个方法的实现类是A类
* 这个回电话的内容result是回答者B提供的,所以这个变量的值是在B类中确定的
*/
public void callBack(String result);
}
[java] view plain copy
/**
* 提问者A类
*/
public class A implements CallBack{
/**
* 提问者A是知道回答者B的联系方式的
* 这里以一个B类引用代表,并在构造方法中传入B实例对象
*/
private B b;
public A(B b){
this.b = b;
}
/**
* 提问者A向回答者B打电话提问题
* 这里以一个call方法表示,并把问题参数传入
*/
public void call(final String question){
/**
* 建立提问者A线程,与回答者B线程结合,构成一个异步的环境
*/
new Thread(new Runnable() {
@Override
public void run() {
/**
* B接电话,听问题
* 这里以调用回答者B的answer方法表示,传入回调方法类参数、问题参数,以表示谁打的电话,问啥了
* 因为A类实现了CallBack接口,所以A类就是回调方法类,回调方法类实现了回调方法
*/
b.answer(A.this, question);
}
}).start();
/**
* 提问者提完问,去干别事情
*/
doOtherThing();
}
public void doOtherThing(){
System.out.println("我是提问者A,我问完问题就去干别的事情了!");
}
/**
* 刚刚说到,这个回电话的方式callBack是提问者A确定的,所以这个方法的实现类是A类
* 所以这里实现回调方法,代表回复的方法是回电话,由回答者B调用
*/
@Override
public void callBack(String result) {
System.out.println("B调用A定义的回调函数:回答者B告诉提问者A,问题的答案是:"+ result);
}
}
[java] view plain copy
/**
* 回答者B类
*/
public class B {
/**
* 回答者B接电话,听问题 这里以调用回答者B的answer方法表示,传入回调方法类、问题参数,以表示谁打的电话,问啥了
*/
public void answer(CallBack callBack, String question) {
System.out.println("A调用B的接电话方法:我是回答者B,提问者A问的问题是:" + question);
/**
* 模拟回答者B先忙自己的事
*/
System.out.println("我是回答者B,我接完电话先去忙我自己的事!");
for (int i = 0; i < 100000; i++) {
}
String result = "2";
System.out.println("我是回答者B,我知道了答案是:" + result);
/**
* 调用回调函数,打电话告知A答案是什么
*/
callBack.callBack(result);
}
}
[java] view plain copy
/**
* 场景测试类
*/
public class test {
public static void main(String args[]){
/**
* 实例化回答者B
*/
B b = new B();
/**
* 实例化提问者A
*/
A a = new A(b);
/**
* A向B提问,开始
*/
a.call("1 + 1 = ?");
}
}
异步回调: |
#include <iostream> #include <stdio.h> #include <stdlib.h> #ifdef _MSC_VER #include <windows.h> #else #include <pthread.h> #endif #ifdef _MSC_VER #define mysleep(n) Sleep(n) #else #define mysleep(n) sleep(n/1000) #endif using namespace std; class xiabo_C{ public: xiabo_C():a(10),d(8) { printf("I am xiabo_C() function\n"); } static int sfunc(); //静态成员函数也遵循public,private,protected访问规则 int func(void); public: int a; static int b ; //此处不能初始化 static const int c = 9;//只有静态常量才能直接初始化 const int d; }; int xiabo_C::b = 11; //静态成员变量的初始化只能这样,不能在构造中初始化 int xiabo_C::sfunc(){ //!!!静态成员函数在类外部实现时,千万不要加static,不要写成了static int sfunc(){},这是内外部的静态C函数 //xiabo::a = 11; //error 静态成员函数不能应用非静态成员 xiabo_C::b = 12; printf("I am static member function,b = %d\n",xiabo_C::b ); return 0; } static int sfunc1(){ printf("I am static function, not member function \n" ); return 0; } int xiabo_C::func(void){ xiabo_C::b = 12; xiabo_C::sfunc(); sfunc1(); return 0; } void test_statichunc(void){ xiabo_C xiabo; xiabo.func(); xiabo_C::sfunc(); //静态成员函数是类的,不是某个对象的,引用必须通过类名来访问 } //------------- class xiabo2_C{ public: typedef int (*pcb)(int a); typedef struct parameter{ int a ; pcb callback; }parameter; xiabo2_C():m_a(1){ } //普通函数 void GetCallBack(parameter* p) // 写回调者实现的回调函数 { m_a = 2; //do something while(1) { printf("GetCallBack print! \n"); mysleep(2000); p->callback(p->a); } } int SetCallBackFun(int a, pcb callback) { printf("SetCallBackFun print! \n"); parameter *p = new parameter ; p->a = 10; p->callback = callback; GetCallBack(p); return 0; } public: int m_a; }; class xiabo2Test_C{ public: xiabo2Test_C():m_b(1){ } static int fCallBack(int a) // 应用者实现的回调函数,静态成员函数,但是不能访问类中非静态成员了,破坏了类的结构 { //do something //m_b = a; // 不能访问类中非静态成员了,破坏了类的结构,应用者使用很麻烦 printf("a = %d\n",a); printf("fCallBack print! \n"); return 0; } public: int m_b; }; //------------------- template<typename Tobject,typename Tparam> class xiabo3_C{ typedef void (Tobject::*Cbfun)(Tparam* ); public: bool Exec(Tparam* pParam); void Set(Tobject *pInstance,Cbfun pFun,Tparam* pParam); private: Cbfun pCbfun; Tobject* m_pInstance; }; template<typename Tobject,typename Tparam> void xiabo3_C<Tobject,Tparam>::Set(Tobject *pInstance,Cbfun pFun,Tparam* pParam){ printf("Set print!\n"); m_pInstance = pInstance; (pInstance->*pFun)(pParam); //可以直接在这里回调传过来的函数指针 pCbfun = pFun; } template<typename Tobject,typename Tparam> bool xiabo3_C<Tobject,Tparam>::Exec(Tparam* pParam){ printf("Exec print!\n"); (m_pInstance->*pCbfun)(pParam);//也可以在这里回调传过来的函数指针 return true; } class xiabo3Test_C{ public: xiabo3Test_C():m_N(13){ } void fCallBack(int *p){ printf("fCallBack : Sum = m_N + *p = %d\n",*p + m_N); printf("fCallBack print! I am a member function! I can access all the member ,HaHa...\n"); } private: int m_N; }; //-------------- //类中定义线程,并实现回调 class xiabo4_C{ public: struct ThreadParam{ xiabo4_C* pthis; int a ; int b ; };//根据线程参数自定义结构 public: xiabo4_C():m_N(1){ } void print(void){ printf("print : m_N = %d \n",m_N); } //静态实现 void CreatAlgorithmThread(void); static void *funThreadAlgorithm(void* p); //静态成员函数实现线程Wrapper //非静态实现 void CreatAlgorithm2Thread(int a ,int b); static void *funThreadAlgorithm2(void* param); //非静态成员函数实现线程Wrapper void ThreadFunc(int a ,int b){ printf("ThreadFunc : I am ThreadFunc,I am a member function! I can access all the member ,HaHa...\n"); printf("ThreadFunc : m_N = %d \n",m_N); } private: int m_N; }; void xiabo4_C::CreatAlgorithmThread(void){ //静态实现 #ifdef _MSC_VER HANDLE handle1 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)funThreadAlgorithm,0,0,NULL); CloseHandle(handle1); #else pthread_t thing1; pthread_create(&thing1,NULL,&funThreadAlgorithm,(void *) 0); pthread_join(thing1,NULL); #endif } void* xiabo4_C::funThreadAlgorithm(void* p){ while(1) { mysleep(2000); printf("I am a static meeber function! I can not access the member\n"); } } void xiabo4_C::CreatAlgorithm2Thread(int a ,int b){ ThreadParam* p = new ThreadParam; p->pthis = this; p->a = a; p->b = b; #ifdef _MSC_VER HANDLE handle2 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)funThreadAlgorithm2,p,0,NULL); CloseHandle(handle2); #else pthread_t thing1; pthread_create(&thing1,NULL,&funThreadAlgorithm2,(void *) p); pthread_join(thing1,NULL); #endif } void* xiabo4_C::funThreadAlgorithm2(void* param){ ThreadParam* p = (ThreadParam*)param; printf("I am a static meeber function! I can not access the member\n"); printf("But I can call a member func ,I can instigate ThreadFunc ,ThreadFunc can access all member\n"); printf("ThreadParam p->a = %d, p->b = %d \n",p->a,p->b); p->pthis->ThreadFunc(p->a,p->b); return 0; } //-------------- //类中定义线程,并实现回调 //A程序员 template<typename Tobject,typename Tparam> class xiabo5_C{ public: struct ThreadParam{ xiabo5_C* pthis; Tparam a ; };//根据线程参数自定义结构 typedef void (Tobject::*Cbfun)(Tparam ); public: xiabo5_C():m_N(1){ printf("xiabo5_C : xiabo5_C()\n"); } void print(void){ printf("print : m_N = %d \n",m_N); } //非静态实现 void CreateCallbackThread(Tobject *pInstance,Cbfun pFun,Tparam a ); static void* funCallbackThread(void* param); //非静态成员函数实现线程Wrapper void ThreadFunc(Tparam a ); //线程执行函数 private: int m_N; Cbfun pCbfun; Tobject* m_pInstance; }; template<typename Tobject,typename Tparam> void xiabo5_C<Tobject,Tparam>:: CreateCallbackThread(Tobject *pInstance,Cbfun pFun,Tparam a ){ ThreadParam* p = new ThreadParam; p->pthis = this; p->a = a; m_pInstance = pInstance; pCbfun = pFun; #ifdef _MSC_VER HANDLE handle2 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)funCallbackThread,p,0,NULL); CloseHandle(handle2); #else pthread_t thing1; pthread_create(&thing1,NULL,&funCallbackThread,(void *) p); //pthread_join(thing1,NULL); //这儿不能阻塞 #endif } template<typename Tobject,typename Tparam> void* xiabo5_C<Tobject,Tparam>::funCallbackThread(void* param){ ThreadParam* p = (ThreadParam*)param; printf("I am a static meeber function! I can not access the member\n"); printf("But I can call a member func ,I can instigate ThreadFunc ,ThreadFunc can access all member\n"); printf("ThreadParam p->a = %d\n",p->a); p->pthis->ThreadFunc(p->a); return 0; } template<typename Tobject,typename Tparam> void xiabo5_C<Tobject,Tparam>::ThreadFunc(Tparam a ){ printf("ThreadFunc : I am ThreadFunc,I am a member function! I can access all the member ,HaHa...\n"); printf("ThreadFunc : m_N = %d \n",m_N); while(1) { mysleep(2000); (m_pInstance->*pCbfun)(a); } } //B程序员 class xiabo5Test_C{ public: xiabo5Test_C():m_N(55){ } void fCallBack(int p){ printf("fCallBack : Sum = m_N + *p = %d\n",p + m_N); printf("fCallBack print! I am a member function! I can access all the member ,HaHa...\n"); } public: private: int m_N; }; int main(void ){ //测试静态成员函数 //test_statichunc(); //测试静态成员函数,用于回调 //xiabo2_C xiabo2; //xiabo2.SetCallBackFun(5,xiabo2Test_C::fCallBack); //测试非静态成员函数,用于回调,good //xiabo3_C<xiabo3Test_C,int> xiabo3; //xiabo3Test_C xiabo3Test; //int p = 13; //xiabo3.Set(&xiabo3Test,&xiabo3Test_C::fCallBack,&p); // //xiabo3.Exec(&p); //类中定义线程 //xiabo4_C xiabo4; //xiabo4.CreatAlgorithm2Thread(1,2); //类中定义线程,并实现回调 xiabo5_C<xiabo5Test_C,int> xiabo5; xiabo5Test_C xiabo5Test; int p = 45; xiabo5.CreateCallbackThread(&xiabo5Test,&xiabo5Test_C::fCallBack,p); xiabo5_C<xiabo5Test_C,int> xiabo51; xiabo5Test_C xiabo5Test1; int p1 = -45; xiabo51.CreateCallbackThread(&xiabo5Test1,&xiabo5Test_C::fCallBack,p1); //getchar(); return 0; }
相关文章推荐
- zookeeper怎么实现的异步操作和回调通知
- android多种方式实现异步加载图片
- zookeeper集群之java程序异步方式实现对数据节点的增,删,改,查及其权限管理(三)
- 跨平台python异步回调机制实现和使用方法
- android 异步方式实现数据加载
- MVC验证10-到底用哪种方式实现客户端服务端双重异步验证
- 使用Guava+Spring实现异步回调操作,提高程序性能
- AndroidUtil - 异步回调中的泛型传递 - 通过父类type方式,规避泛型擦除
- Java线程间通信-回调的实现方式
- Java线程间通信-回调的实现方式
- 带你一步步实现线程池异步回调
- ES transport client底层是netty实现,netty本质上是异步方式,但是netty自身可以使用sync或者await(future超时机制)来实现类似同步调用!因此,ES transport client可以同步调用也可以异步(不过底层的socket必然是异步实现)
- object-c中通过异步方式实现下载
- C++中实现回调机制的几种方式[转]
- Swift 异步加载回调 callBack 实现
- C++中实现回调机制的几种方式
- Java线程间通信-回调的实现方式
- 在 ASP.NET 网页中不经过回发而以编程方式实现客户端回调
- AsyncTask和Handler两种异步方式的实现和区别比较
- C#异步编程的实现方式(1)——异步委托