Delphi 回调函数及线程使用回调函数

(转自:https://www.cnblogs.com/findumars/p/5294117.html,如有侵权,请联系我,马上删除)

原因:在写线程时,用beginThread函数和creatthread函数可以实现函数调用及被调用函数的参数传递(用一个结构体,或数组进行参数传递)。在用线程类的时候,用回调函数的方法时,没法传递被调用函数参数,故写此文。下文自己的demo,测试传递readCount数值

Delphi回调函数及其使用
1 回调函数的概述

回调函数是这样一种机制:调用者在初始化一个对象(这里的对象是泛指,包括OOP中的对象、全局函数等)时,将一些参数传递给对象,同时将一个调用者可以访问的函数地址传递给该对象。这个函数就是调用者和被调用者之间的一种通知约定,当约定的事件发生时,被调用者(一般会包含一个工作线程)就会按照回调函数地址调用该函数。
这种方式,调用者在一个线程,被调用者在另一个线程。

消息:

消息也可以看作是某种形式的回调,因为消息也是在初始化时由调用者向被调用者传递一个句柄和一个消息编号,在约定的事件发生时被调用者向调用者发送消息。
  这种方式,调用者在主线程中,被调用者在主线程或者工作线程中。

Delphi事件模型:

在Delphi的VCL中有很多可视化组件都是使用事件模型,例如TForm的OnCreate事件,其原理是:在设计时指定事件函数,在运行时事件触发,则会调用在设计时指定的事件函数。

在机制上,Delphi事件模型与回调是一样的。但具体形式有些区别,纯的回调函数是全局函数的形式,而Delphi事件是对象方法的形式,即可以定义如下回调函数类型
  type

TCallBackFunc = procedure (pData: Pointer) of object;

2 回调函数的使用说明

回调函数主要在两个场合使用,第一个是某些Windows的API要求用回调函数作为其参数地址,另一种是用户在某种特定的场合定义的某个函数需要使用回调函数作为其参数地址,对于用户的定义的函数来说,一般是当调用动态连接库中的函数时使用。

对于使用一个回调函数主要有以下几个步骤:

1、定义一个回调函数类型,跟一般的函数过程的定义并没有什么区别,但其定义必须根据需要满足回调函数的函数要求,唯一的区别在于在函数或过程的定义后面必须声明其为windows标准调用;

例:

type

THDFunction= function(I:integer;s:string):integer; stdcall;

对于过程的声明:

type

THDProcedure=procedure(s:string); stdcall;

2、 然后根据此原形定义一个相应的函数或过程,对于这个函数或过程来说名字没有什么要求,对函数其参数的类型和返回值的类型必须和定义的回调函数类型完全一致,对于过程来说,只需要其参数类型一样就可以了。

例:根据上面的函数和过程的原形定义一个相应的函数和一个相应的过程。

函数原形定义:

Function HdFunExample(k:integer,sExam:string):integer; stdcall;

过程定义:

procedure HdProExample(sExam:string);stdcall;

3、 在程序中实现此回调函数或着过程;

Function HdFunExample(k:integer,sExam:string):integer; stdcall;

Begin

End;

procedure HdProExample(sExam:string);stdcall;

begin

end;

4、 调用过程;

回调函数一般作为系统的某个函数的入口地址;

根据调用函数的原形:

假设有如下调用函数:

function DyHdFunExample(HdFun:THDFunction;I:integer):boolean;

注:

在调用函数中通过对函数指针的处理可以直接调用回调函数(即调用函数中的那个是回调函数类型的参数,直接操作它),使回调函数履行一定的操作。即在调用函数中实现回调函数的功能。

调用:

var

I:integer;

begin

I:=DyHdFunExample(@HdFunExample,i);

//…….

End;

3 举例说明

示例程序在H:/ 回调函数示例/ 目录下面。

回调函数的使用主要在于Windows原有的API函数,但对于用户的自定义的调用函数一般在于动态连接库中。常规的同一个工程下面一般不需要使用回调函数。(个人认为).。

功能大体描述:Form1中有一个Edit和一个Button,当点击BUTTON时弹出FORM2,FORM2中也有一个EDIT和一个BUTTON,当点击FORM2中的BUTTON时,将FORM2中的EDIT的TEXT属性赋值给FORM1中的EDIT的TEXT。

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,StdCtrls;

type
TForm1 = class(TForm)
{主窗体中放一个Edit和一个Button}
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
{定义一个用于回调的过程}
procedure test(str:string);
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation
{引用unit2}
uses unit2;
{$R *.dfm}
{回调过程的实现部分}
procedure TForm1.test(str: string);
begin
{将str值副给Edit1}
Edit1.Text:=str;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
{调用Unit2的接口方法}
CallUnit2(test);
end;

end.

unit Unit2;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
{定义一个回调函数类型}
TFuncCallBack=procedure(str:string) of object;
TForm2 = class(TForm)
{Form2中也有一个Edit和一个Button}
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
{定义一个回调函数类型的变量}
aFuncCallBack:TFuncCallBack;
public
{ Public declarations }
end;
{提供给Unit1调用的接口方法,注意里面的参数的类型}
procedure CallUnit2(FuncCallBack:TFuncCallBack);

var
Form2: TForm2;

implementation

{$R *.dfm}
{接口方法的实现部分}
procedure CallUnit2(FuncCallBack:TFuncCallBack);
begin
Application.CreateForm(TForm2,Form2);
{将参数赋值给FuncCallBack}
Form2.aFuncCallBack:=FuncCallBack;

Form2.ShowModal;
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
{当点击Form2的按钮时将Form2中的Edit的值传递给了Form1中的Edit}
{是不是很神奇?我并没有uses Unit1,但却改变了Form1中Edit的Text属性}
aFuncCallBack(Edit1.Text);
ModalResult:=mrOk;
end;

end.

http://blog.csdn.net/tercel99/article/details/4624686

http://blog.csdn.net/tercel99/article/details/4622207

自己的一个小demo:
第一个单元

unit UThreadSave;

interface

uses
  System.Classes;

type
  TMyfunc = procedure(readCount:Integer);stdcall;  //标准调用,全局函数
  TObjFunc = procedure(readCount:integer) of object;  //对象方法调用

  TThreadSave = class(TThread)
  private
    { Private declarations }
    FreadCount:Integer;
    FMyfunc : TMyfunc;
    FObjfunc : TObjFunc;
  protected
    procedure Execute; override;
  public
    constructor create(Issuspend:boolean);
    destructor destroy;override;
    procedure SetThreadFunc(func: Pointer);
    procedure SetObjFunc(func: TObjFunc);

    property myReadCount:Integer read FreadCount write FreadCount;

  end;

implementation

{
  Important: Methods and properties of objects in visual components can only be
  used in a method called using Synchronize, for example,

      Synchronize(UpdateCaption);

  and UpdateCaption could look like,

    procedure TThreadSave.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end;
    
    or 

    Synchronize(
      procedure
      begin
        Form1.Caption := 'Updated in thread via an anonymous method'
      end
      )
    );

  where an anonymous method is passed.

  Similarly, the developer can call the Queue method with similar parameters as 
  above, instead passing another TThread class as the first parameter, putting
  the calling thread in a queue with the other thread.

}

{ TThreadSave }

constructor TThreadSave.create(Issuspend: boolean);
begin
  inherited create(Issuspend);
end;

destructor TThreadSave.destroy;
begin

  inherited;
end;

procedure TThreadSave.Execute;
begin
  { Place thread code here }
  FreeOnTerminate:=True;
  while not Terminated do
  begin
    if Assigned(FObjfunc) then
      FObjfunc(myReadCount);
    Sleep(100);
  end;
end;

procedure TThreadSave.SetObjFunc(func: TObjFunc);
begin
   FObjfunc := func;
end;

procedure TThreadSave.SetThreadFunc(func: Pointer);
begin
  FMyfunc := func;

end;

end.

第二个单元

unit USave2File;

interface
uses UDataStructure, UThreadSave, classes, System.StrUtils, Winapi.Windows;

type

 TClassSave = class

    private
       FgetUnitLen:Integer;
       FThreadSave: TThreadSave;
       procedure Save2File(readCount:Integer);  //对象方法调用写为对象方法
    public
      constructor create;
      destructor destroy;override;
      procedure StartSave;
 end;
//procedure Save2File(readCount:Integer);stdcall;  //标准回调函数应该写为全局函数

implementation
uses UGlobalPara;
{ TClassSave }

constructor TClassSave.create;
begin
  FgetUnitLen:=800;
  FThreadSave:=TThreadSave.create(True);
//  FThreadSave.SetThreadFunc(@Save2file);
  FThreadSave.SetObjFunc(Save2file);
end;

destructor TClassSave.destroy;
begin

  inherited;
end;

//对象方法对应的实现函数
procedure TClassSave.Save2File(readCount:Integer);
var
  tempstream:TMemorystream;
  tempLen:Integer;
  tempIntArr:TIntArr;
  tempInt:Integer;
  tempGetResult:Integer;
  I: Integer;
begin
  tempLen:=SizeOf(Integer)*readCount;
  tempGetResult:=FDataCache.GetDataCache(eSaveData,eIntCache,tempstream,tempLen);
  if tempGetResult<>0 then
  begin
    SetLength(tempIntArr,readCount);
    tempstream.Position:=0;
    for I := 0 to readCount do
    begin
      tempstream.ReadBuffer(tempInt,SizeOf(Integer));
      tempIntArr[i]:=tempInt;
    end;

  end;

end;

//全局回调函数对应的实现
//procedure Save2File(readCount: Integer);
//var
//  tempstream: TMemorystream;
//  tempLen: Integer;
//  tempIntArr: TIntArr;
//  tempInt: Integer;
//  tempGetResult: Integer;
//  I: Integer;
//begin
//  tempLen := SizeOf(Integer) * readCount;     
//  tempGetResult := FDataCache.GetDataCache(eSaveData, eIntCache, tempstream, tempLen);
//  if tempGetResult <> 0 then
//  begin
//    SetLength(tempIntArr, readCount);
//    tempstream.Position := 0;
//    for I := 0 to readCount do
//    begin
//      tempstream.ReadBuffer(tempInt, SizeOf(Integer));
//      tempIntArr[I] := tempInt;
//    end;
//  end;
//end;


procedure TClassSave.StartSave;
begin
  FThreadSave.myReadCount:=FgetUnitLen;
  FThreadSave.Resume;
end;

end.

测试 结果为,readCount 可以正确传递值800!

(本人菜鸟,如有写错,敬请指正,谢谢!)

猜你喜欢

转载自blog.csdn.net/qq_15258081/article/details/87912252
今日推荐