• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            奇奇的空間

            奇奇的空間
            posts - 5, comments - 13, trackbacks - 0, articles - 23

            回調(diào)機(jī)制

            Posted on 2006-02-21 10:26 奇奇 閱讀(688) 評(píng)論(0)  編輯 收藏 引用 所屬分類: VC++
            軟件模塊之間總是存在著一定的接口,從調(diào)用方式上,可以把他們分為三類:同步調(diào)用、回調(diào)和異步調(diào)用。同步調(diào)用是一種阻塞式調(diào)用,調(diào)用方要等待對方執(zhí)行完畢才返回,它是一種單向調(diào)用;回調(diào)是一種雙向調(diào)用模式,也就是說,被調(diào)用方在接口被調(diào)用時(shí)也會(huì)調(diào)用對方的接口;異步調(diào)用是一種類似消息或事件的機(jī)制,不過它的調(diào)用方向剛好相反,接口的服務(wù)在收到某種訊息或發(fā)生某種事件時(shí),會(huì)主動(dòng)通知客戶方(即調(diào)用客戶方的接口)。回調(diào)和異步調(diào)用的關(guān)系非常緊密,通常我們使用回調(diào)來實(shí)現(xiàn)異步消息的注冊,通過異步調(diào)用來實(shí)現(xiàn)消息的通知。同步調(diào)用是三者當(dāng)中最簡單的,而回調(diào)又常常是異步調(diào)用的基礎(chǔ),因此,下面我們著重討論回調(diào)機(jī)制在不同軟件架構(gòu)中的實(shí)現(xiàn)。

            1 什么是回調(diào)
            軟件模塊之間總是存在著一定的接口,從調(diào)用方式上,可以把他們分為三類:同步調(diào)用、回調(diào)和異步調(diào)用。同步調(diào)用是一種阻塞式調(diào)用,調(diào)用方要等待對方執(zhí)行完畢才返回,它是一種單向調(diào)用;回調(diào)是一種雙向調(diào)用模式,也就是說,被調(diào)用方在接口被調(diào)用時(shí)也會(huì)調(diào)用對方的接口;異步調(diào)用是一種類似消息或事件的機(jī)制,不過它的調(diào)用方向剛好相反,接口的服務(wù)在收到某種訊息或發(fā)生某種事件時(shí),會(huì)主動(dòng)通知客戶方(即調(diào)用客戶方的接口)。回調(diào)和異步調(diào)用的關(guān)系非常緊密,通常我們使用回調(diào)來實(shí)現(xiàn)異步消息的注冊,通過異步調(diào)用來實(shí)現(xiàn)消息的通知。同步調(diào)用是三者當(dāng)中最簡單的,而回調(diào)又常常是異步調(diào)用的基礎(chǔ),因此,下面我們著重討論回調(diào)機(jī)制在不同軟件架構(gòu)中的實(shí)現(xiàn)。


            對于不同類型的語言(如結(jié)構(gòu)化語言和對象語言)、平臺(tái)(Win32、JDK)或構(gòu)架(CORBA、DCOM、WebService),客戶和服務(wù)的交互除了同步方式以外,都需要具備一定的異步通知機(jī)制,讓服務(wù)方(或接口提供方)在某些情況下能夠主動(dòng)通知客戶,而回調(diào)是實(shí)現(xiàn)異步的一個(gè)最簡捷的途徑。

            對于一般的結(jié)構(gòu)化語言,可以通過回調(diào)函數(shù)來實(shí)現(xiàn)回調(diào)。回調(diào)函數(shù)也是一個(gè)函數(shù)或過程,不過它是一個(gè)由調(diào)用方自己實(shí)現(xiàn),供被調(diào)用方使用的特殊函數(shù)。

            在面向?qū)ο蟮恼Z言中,回調(diào)則是通過接口或抽象類來實(shí)現(xiàn)的,我們把實(shí)現(xiàn)這種接口的類成為回調(diào)類,回調(diào)類的對象成為回調(diào)對象。對于象C++或Object Pascal這些兼容了過程特性的對象語言,不僅提供了回調(diào)對象、回調(diào)方法等特性,也能兼容過程語言的回調(diào)函數(shù)機(jī)制。

            Windows平臺(tái)的消息機(jī)制也可以看作是回調(diào)的一種應(yīng)用,我們通過系統(tǒng)提供的接口注冊消息處理函數(shù)(即回調(diào)函數(shù)),從而實(shí)現(xiàn)接收、處理消息的目的。由于Windows平臺(tái)的API是用C語言來構(gòu)建的,我們可以認(rèn)為它也是回調(diào)函數(shù)的一個(gè)特例。

            對于分布式組件代理體系CORBA,異步處理有多種方式,如回調(diào)、事件服務(wù)、通知服務(wù)等。事件服務(wù)和通知服務(wù)是CORBA用來處理異步消息的標(biāo)準(zhǔn)服務(wù),他們主要負(fù)責(zé)消息的處理、派發(fā)、維護(hù)等工作。對一些簡單的異步處理過程,我們可以通過回調(diào)機(jī)制來實(shí)現(xiàn)。

            下面我們集中比較具有代表性的語言(C、Object Pascal)和架構(gòu)(CORBA)來分析回調(diào)的實(shí)現(xiàn)方式、具體作用等。

            2 過程語言中的回調(diào)(C)

            2.1 函數(shù)指針
            回調(diào)在C語言中是通過函數(shù)指針來實(shí)現(xiàn)的,通過將回調(diào)函數(shù)的地址傳給被調(diào)函數(shù)從而實(shí)現(xiàn)回調(diào)。因此,要實(shí)現(xiàn)回調(diào),必須首先定義函數(shù)指針,請看下面的例子:

            
            void Func(char *s);// 函數(shù)原型
            void (*pFunc) (char *);//函數(shù)指針
            

            可以看出,函數(shù)的定義和函數(shù)指針的定義非常類似。

            一般的化,為了簡化函數(shù)指針類型的變量定義,提高程序的可讀性,我們需要把函數(shù)指針類型自定義一下。

            
            typedef void(*pcb)(char *);
            

            回調(diào)函數(shù)可以象普通函數(shù)一樣被程序調(diào)用,但是只有它被當(dāng)作參數(shù)傳遞給被調(diào)函數(shù)時(shí)才能稱作回調(diào)函數(shù)。

            被調(diào)函數(shù)的例子:

            
            void GetCallBack(pcb callback)
            {
            /*do something*/
            }
            用戶在調(diào)用上面的函數(shù)時(shí),需要自己實(shí)現(xiàn)一個(gè)pcb類型的回調(diào)函數(shù):
            void fCallback(char *s) 
            {
            /* do something */
            } 
            然后,就可以直接把fCallback當(dāng)作一個(gè)變量傳遞給GetCallBack,
            GetCallBack(fCallback);
            

            如果賦了不同的值給該參數(shù),那么調(diào)用者將調(diào)用不同地址的函數(shù)。賦值可以發(fā)生在運(yùn)行時(shí),這樣使你能實(shí)現(xiàn)動(dòng)態(tài)綁定。

            2.2 參數(shù)傳遞規(guī)則
            到目前為止,我們只討論了函數(shù)指針及回調(diào)而沒有去注意ANSI C/C++的編譯器規(guī)范。許多編譯器有幾種調(diào)用規(guī)范。如在Visual C++中,可以在函數(shù)類型前加_cdecl,_stdcall或者_(dá)pascal來表示其調(diào)用規(guī)范(默認(rèn)為_cdecl)。C++ Builder也支持_fastcall調(diào)用規(guī)范。調(diào)用規(guī)范影響編譯器產(chǎn)生的給定函數(shù)名,參數(shù)傳遞的順序(從右到左或從左到右),堆棧清理責(zé)任(調(diào)用者或者被調(diào)用者)以及參數(shù)傳遞機(jī)制(堆棧,CPU寄存器等)。

            將調(diào)用規(guī)范看成是函數(shù)類型的一部分是很重要的;不能用不兼容的調(diào)用規(guī)范將地址賦值給函數(shù)指針。例如:

            
            // 被調(diào)用函數(shù)是以int為參數(shù),以int為返回值
            __stdcall int callee(int); 
            
            // 調(diào)用函數(shù)以函數(shù)指針為參數(shù)
            void caller( __cdecl int(*ptr)(int)); 
            
            // 在p中企圖存儲(chǔ)被調(diào)用函數(shù)地址的非法操作
            __cdecl int(*p)(int) = callee; // 出錯(cuò)
            

            指針p和callee()的類型不兼容,因?yàn)樗鼈冇胁煌恼{(diào)用規(guī)范。因此不能將被調(diào)用者的地址賦值給指針p,盡管兩者有相同的返回值和參數(shù)列

            2.3 應(yīng)用舉例
            C語言的標(biāo)準(zhǔn)庫函數(shù)中很多地方就采用了回調(diào)函數(shù)來讓用戶定制處理過程。如常用的快速排序函數(shù)、二分搜索函數(shù)等。

            快速排序函數(shù)原型:

            
            void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
            二分搜索函數(shù)原型:
            void *bsearch(const void *key, const void *base, size_t nelem,
            				 size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
            

            其中fcmp就是一個(gè)回調(diào)函數(shù)的變量。

            下面給出一個(gè)具體的例子:

            
            #include <stdio.h>
            #include <stdlib.h>
            
            int sort_function( const void *a, const void *b);
            int list[5] = { 54, 21, 11, 67, 22 };
            
            int main(void)
            {
               int  x;
            
               qsort((void *)list, 5, sizeof(list[0]), sort_function);
               for (x = 0; x < 5; x++)
                  printf("%i\n", list[x]);
               return 0;
            }
            
            int sort_function( const void *a, const void *b)
            {
               return *(int*)a-*(int*)b;
            }
            

            2.4 面向?qū)ο笳Z言中的回調(diào)(Delphi)

            Dephi與C++一樣,為了保持與過程語言Pascal的兼容性,它在引入面向?qū)ο髾C(jī)制的同時(shí),保留了以前的結(jié)構(gòu)化特性。因此,對回調(diào)的實(shí)現(xiàn),也有兩種截然不同的模式,一種是結(jié)構(gòu)化的函數(shù)回調(diào)模式,一種是面向?qū)ο蟮慕涌谀J健?

            2.4.1 回調(diào)函數(shù)

            回調(diào)函數(shù)類型定義:

            
            type
               TCalcFunc=function (a:integer;b:integer):integer;
            

            按照回調(diào)函數(shù)的格式自定義函數(shù)的實(shí)現(xiàn),如

            
            function Add(a:integer;b:integer):integer
            begin
              result:=a+b;
            end;
            function Sub(a:integer;b:integer):integer
            begin
              result:=a-b;
            end;
            

            回調(diào)的使用

            
            function Calc(calc:TcalcFunc;a:integer;b:integer):integer
            

            下面,我們就可以在我們的程序里按照需要調(diào)用這兩個(gè)函數(shù)了

            
            c:=calc(add,a,b);//c=a+b
            c:=calc(sub,a,b);//c=a-b
            

            2.4.2 回調(diào)對象

            什么叫回調(diào)對象呢,它具體用在哪些場合?首先,讓我們把它與回調(diào)函數(shù)對比一下,回調(diào)函數(shù)是一個(gè)定義了函數(shù)的原型,函數(shù)體則交由第三方來實(shí)現(xiàn)的一種動(dòng)態(tài)應(yīng)用模式。要實(shí)現(xiàn)一個(gè)回調(diào)函數(shù),我們必須明確知道幾點(diǎn):該函數(shù)需要那些參數(shù),返回什么類型的值。同樣,一個(gè)回調(diào)對象也是一個(gè)定義了對象接口,但是沒有具體實(shí)現(xiàn)的抽象類(即接口)。要實(shí)現(xiàn)一個(gè)回調(diào)對象,我們必須知道:它需要實(shí)現(xiàn)哪些方法,每個(gè)方法中有哪些參數(shù),該方法需要放回什么值。

            因此,在回調(diào)對象這種應(yīng)用模式中,我們會(huì)用到接口。接口可以理解成一個(gè)定義好了但是沒有實(shí)現(xiàn)的類,它只能通過繼承的方式被別的類實(shí)現(xiàn)。Delphi中的接口和COM接口類似,所有的接口都繼承與IInterface(等同于IUnknow),并且要實(shí)現(xiàn)三個(gè)基本的方法QueryInterface, _AddRef, 和_Release。

            • 定義一個(gè)接口
              
              type IShape=interface(IInterface)
              	procedure Draw;
              end
              
            • 實(shí)現(xiàn)回調(diào)類
              
              type TRect=class(TObject,IShape)
              	protected
                    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
                    function _AddRef: Integer; stdcall;
              function _Release: Integer; stdcall;
                  public
              	  procedure Draw;
              end;
              
              type TRound=class(TObject,IShape)
              	protected
                    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
                    function _AddRef: Integer; stdcall;
              function _Release: Integer; stdcall;
                  public
              	  procedure Draw;
              end;
              
            • 使用回調(diào)對象
              
              procedure MyDraw(shape:IShape);
              var 
              shape:IShape;
              begin
              shape.Draw; 
              end;
              

            如果傳入的對象為TRect,那么畫矩形;如果為TRound,那么就為圓形。用戶也可以按照自己的意圖來實(shí)現(xiàn)IShape接口,畫出自己的圖形:

            
            MyDraw(Trect.Create);
            MyDraw(Tround.Create);
            

            2.4.3 回調(diào)方法

            回調(diào)方法(Callback Method)可以看作是回調(diào)對象的一部分,Delphi對windows消息的封裝就采用了回調(diào)方法這個(gè)概念。在有些場合,我們不需要按照給定的要求實(shí)現(xiàn)整個(gè)對象,而只要實(shí)現(xiàn)其中的一個(gè)方法就可以了,這是我們就會(huì)用到回調(diào)方法。

            回調(diào)方法的定義如下:

            
            TNotifyEvent = procedure(Sender: TObject) of object; 
            TMyEvent=procedure(Sender:Tobject;EventId:Integer) of object;
            

            TNotifyEvent 是Delphi中最常用的回調(diào)方法,窗體、控件的很多事件,如單擊事件、關(guān)閉事件等都是采用了TnotifyEvent。回調(diào)方法的變量一般通過事件屬性的方式來定義,如TCustomForm的創(chuàng)建事件的定義:

            
            property OnCreate: TNotifyEvent read FOnCreate write FOnCreate stored IsForm;
            

            我們通過給事件屬性變量賦值就可以定制事件處理器。

            用戶定義對象(包含回調(diào)方法的對象):

            
            type TCallback=Class
                procedure ClickFunc(sender:TObject);
            end;
            procedure Tcallback.ClickFunc(sender:TObject);
            begin
              showmessage('the caller is clicked!');
            end;
            

            窗體對象:

            
            type TCustomFrm=class(TForm)
              public
            	procedure RegisterClickFunc(cb:procedure(sender:Tobject) of object);
            end;
            
            procedure TcustomFrm..RegisterClickFunc(cb:TNotifyEvent);
            begin
              self.OnClick=cb;
            end;
            

            使用方法:

            
            var
              frm:TcustomFrm;
            begin
              frm:=TcustomFrm.Create(Application);
              frm.RegisterClickFunc(Tcallback.Create().ClickFunc);
            end;
            

            3 回調(diào)在分布式計(jì)算中的應(yīng)用(CORBA)

            3.1 回調(diào)接口模型
            CORBA的消息傳遞機(jī)制有很多種,比如回調(diào)接口、事件服務(wù)和通知服務(wù)等。回調(diào)接口的原理很簡單,CORBA客戶和服務(wù)器都具有雙重角色,即充當(dāng)服務(wù)器也是客戶客戶。

            回調(diào)接口的反向調(diào)用與正向調(diào)用往往是同時(shí)進(jìn)行的,如果服務(wù)端多次調(diào)用該回調(diào)接口,那么這個(gè)回調(diào)接口就變成異步接口了。因此,回調(diào)接口在CORBA中常常充當(dāng)事件注冊的用途,客戶端調(diào)用該注冊函數(shù)時(shí),客戶函數(shù)就是回調(diào)函數(shù),在此后的調(diào)用中,由于不需要客戶端的主動(dòng)參與,該函數(shù)就是實(shí)現(xiàn)了一種異步機(jī)制。

            從CORBA規(guī)范我們知道,一個(gè)CORBA接口在服務(wù)端和客戶端有不同的表現(xiàn)形式,在客戶端一般使用樁(Stub)文件,服務(wù)端則用到框架(Skeleton)文件,接口的規(guī)格采用IDL來定義。而回調(diào)函數(shù)的引入,使得服務(wù)端和客戶端都需要實(shí)現(xiàn)一定的樁和框架。下面是回調(diào)接口的實(shí)現(xiàn)模型:

            3.1.1 范例

            下面給出了一個(gè)使用回調(diào)的接口文件,服務(wù)端需要實(shí)現(xiàn)Server接口的框架,客戶端需要實(shí)現(xiàn)CallBack的框架:

            
            module cb
            {
            	interface CallBack;
            	interface Server;
            
            interface CallBack 
            {
                	void OnEvent(in long Source,in long msg);
            };
              	interface Server 
            {
                	long RegisterCB(in CallBack cb);
            		void UnRegisterCB(in long hCb);
            };
            };
            

            客戶端首先通過同步方式調(diào)用服務(wù)端的接口RegistCB,用來注冊回調(diào)接口CallBack。服務(wù)端收到該請求以后,就會(huì)保留該接口引用,如果發(fā)生某種事件需要向客戶端通知的時(shí)候就通過該引用調(diào)用客戶方的OnEvent函數(shù),以便對方及時(shí)處理。  

            久久人妻少妇嫩草AV无码专区| 成人久久精品一区二区三区| 久久99精品国产麻豆婷婷| 亚洲狠狠久久综合一区77777 | 久久精品国产福利国产琪琪| 品成人欧美大片久久国产欧美| 一级做a爰片久久毛片毛片| 久久天天躁狠狠躁夜夜网站 | 国产精品久久久久影院嫩草| 久久久精品久久久久特色影视| 亚洲欧美伊人久久综合一区二区 | 四虎影视久久久免费| 97精品久久天干天天天按摩| 久久影院午夜理论片无码| 91精品国产综合久久久久久| 久久久久久久久波多野高潮| 大蕉久久伊人中文字幕| 久久久精品2019免费观看| 国产精品久久久久蜜芽| 久久人人爽人人澡人人高潮AV| 97久久香蕉国产线看观看| 亚洲精品无码成人片久久| 久久久久综合国产欧美一区二区| 久久久国产乱子伦精品作者| 久久人人爽人人爽人人av东京热 | 亚洲国产成人久久精品99| 久久精品9988| 国产99精品久久| 国产精品久久久久AV福利动漫| 久久久久久久97| 97精品伊人久久大香线蕉| 精品久久久一二三区| 四虎国产精品成人免费久久| 青青草原综合久久大伊人导航| 91久久国产视频| 久久久久黑人强伦姧人妻| 精品久久久久久无码免费| 97久久精品人人做人人爽| 国产亚洲精午夜久久久久久| 久久精品视屏| 久久免费看黄a级毛片|