惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
IT之家
IT之家
博客园 - Franky
Stack Overflow Blog
Stack Overflow Blog
宝玉的分享
宝玉的分享
Recent Announcements
Recent Announcements
Engineering at Meta
Engineering at Meta
S
SegmentFault 最新的问题
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
H
Help Net Security
V
V2EX
H
Hackread – Cybersecurity News, Data Breaches, AI and More
量子位
博客园 - 叶小钗
J
Java Code Geeks
博客园 - 【当耐特】
月光博客
月光博客
爱范儿
爱范儿
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
小众软件
小众软件

博客园 - 张皓

Stereoscopic Player 1.7.4 (SSP) 加载字幕 layout_weight体验(实现按比例显示) Linux5配置jboss环境 pager-taglib 分页扩展实例 DisplayTag1.2 扩展(自定义分页、排序、导出、页面导航) EJB3在JBoss5内集群探究 maven2 Jetty运行多模块的web application JBoss5开发web service常见问题 jboss启动常见的错误 ADO连接池 IDUdpServer研究心得 DBGrid内使用CheckBox功能 子绑定控件获取父绑定项的值 - 张皓 - 博客园 IdTcpServer 用户掉线检测方法 . Net环境下消息队列(MSMQ)对象的应用[转] 消息队列(Message Queue)简介及其使用[转] 分布式事务TransactionScope小结[转] 分布式事务的点滴 调试SQL Server存储过程方法
DLL内线程同步主线程研究(子线程代码放到主线程执行)
张皓 · 2010-07-29 · via 博客园 - 张皓

DLL内线程同步主线程研究(子线程代码放到主线程执行)

       我们在实际项目中经常会用到多线程编程,比如Socket编程等,在创建的线程内同步主线程一般使用Synchronize方法实现子线程操作放到主线程执行,Synchronize使用非常方便,且在2009及以上版本都可以使用匿名方法,这样给我们多线程带来了很大的便利。但是实践证明Synchronize只在主程序内正常工作。如果在主程序加载的DLL程序内运行使用Synchronize方法要求的条件比较苛刻,它要求必须把DLL程序拷挂到主程序,同时DLL内有窗体状态需为Modal或者主程序内窗体无一显示。具体见下:

主程序:

unit MainFrm;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, StdCtrls;

type

  TMainForm = class(TForm)

    btn1: TButton;

    procedure btn1Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;

var

  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.btn1Click(Sender: TObject);

var

  FHandle:THandle;

  OpenDLLWindow:procedure(AMainHandle:Integer);stdcall;

begin

  FHandle:=LoadLibrary(PChar('DLLPrj.dll'));

  if FHandle>0 then

  begin

    OpenDLLWindow:= GetProcAddress(FHandle,PChar('OpenDLLWindow'));

    if Assigned(OpenDLLWindow) then

    begin

      OpenDLLWindow(Application.Handle);

    end;

  end

  else

    ShowMessage('加载DLL失败!');

end;

end.

DLL程序:

unit DLLFrm;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs;

type

  TDLLForm = class(TForm)

    procedure FormShow(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;

type

  TDLLThread = class(TThread)

  protected

    procedure Execute; override;

  end;

var

  DLLForm: TDLLForm;

implementation

{$R *.dfm}

procedure TDLLThread.Execute;

var

  Count:Integer;

  tp:TThreadProcedure;

begin

  Count:=0;

  while True do

  begin

    try

      Synchronize(

        procedure

        begin

          ShowMessage(IntToStr(Count));

          Count:=Count+2000;

        end

      );

    except

      // ignore error

    end;

    Sleep(2000);// 暂停两秒

  end;

end;

procedure OpenDLLWindow(AMainHandle:Integer);stdcall;

begin

  if DLLForm=nil then

DLLForm:=TDLLForm.Create(Application);

// 拷挂到主程序

  Application.Handle:=AMainHandle;

  DLLForm.ShowModal;

  //DLLForm.Show;

end;

exports OpenDLLWindow;

procedure TDLLForm.FormShow(Sender: TObject);

var

  DLLThread :TDLLThread;

begin

  DLLThread:=TDLLThread.Create(False);

end;

end.

以上代码使用了Synchronize方法进行同步主线程,可以正常运行。但如果我们把DLL项目内方法OpenDLLWindow内去掉Application.Handle:=AMainHandle;或者把代码DLLForm.ShowModal;改为DLLForm.Show;这样线程同步主线程时将被阻塞。

       这样看来如果如果在DLL项目内如果被主程序加载后窗体显示方式不是Modal的话我们将无法使用便利的Synchronize方法。这样我们使用SendMessage往主线程发送消息成为必须,如我们把DLL工程内代码更改如下:

unit DLLFrm;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs;

type

  TDLLForm = class(TForm)

    procedure FormShow(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

    procedure WMRefreshForm(var Msg: TMessage); message WM_USER+100;

  end;

type

  TDLLThread = class(TThread)

  protected

    procedure Execute; override;

  end;

var

  DLLForm: TDLLForm;

implementation

{$R *.dfm}

procedure TDLLThread.Execute;

var

  Count:Integer;

  tp:TThreadProcedure;

begin

  Count:=0;

  while True do

  begin

    try

      SendMessage(DLLForm.Handle, WM_USER+100, Count, 0);

      Count:=Count+2000;

    except

      // ignore error

    end;

    Sleep(2000);

  end;

end;

procedure OpenDLLWindow(AMainHandle:Integer);stdcall;

begin

  if DLLForm=nil then

    DLLForm:=TDLLForm.Create(Application);

  // 拷挂到主程序

  //Application.Handle:=AMainHandle;

  //DLLForm.ShowModal;

  DLLForm.Show;

end;

exports OpenDLLWindow;

procedure TDLLForm.FormShow(Sender: TObject);

var

  DLLThread :TDLLThread;

begin

  DLLThread:=TDLLThread.Create(False);

end;

procedure TDLLForm.WMRefreshForm(var Msg: TMessage);

begin

  if Msg.Msg=WM_USER+100 then

  begin

    ShowMessage(IntToStr(Msg.WParam));

  end;

end;

end.

这样我们的DLL程序就工作正常了,这也是目前的常用方法。但是如果我们在线程内有频繁的同步操作,或者这些同步操作会用到比较多的线程内变量,这样SendMessage就显得麻烦又吃力。如果我们在线程执行方法内能自定义匿名方法就像使用Synchronize那样的话我们的代码量将大大减少,且编程过程将大大简化,这样我们就想到了把匿名方法的指针作为SendMessage的一个参数传到自定义的消息内,然后在自定义消息内执行这个匿名方法。令我们庆幸的是这样是行的通的。如我们把DLL工程内代码修改如下:

unit DLLFrm;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs;

type

  TDLLForm = class(TForm)

    procedure FormShow(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

    procedure WMRefreshForm(var Msg: TMessage); message WM_USER+100;

  end;

type

  TDLLThread = class(TThread)

  protected

    procedure Execute; override;

  end;

var

  DLLForm: TDLLForm;

implementation

{$R *.dfm}

procedure TDLLThread.Execute;

var

  Count:Integer;

  tp:TThreadProcedure;

begin

  Count:=0;

  while True do

  begin

    try

      SendMessage(DLLForm.Handle, WM_USER+100,Integer(

        @procedure

        begin

          ShowMessage(IntToStr(Count));

          Count:=Count+2000;

        end),0

      );

//      tp:= procedure

//      begin

//        ShowMessage(IntToStr(Count));

//        Count:=Count+2000;

//      end;

//      SendMessage(DLLForm.Handle, WM_USER+100,Integer(@tp),0);

    except

      // ignore error

    end;

    Sleep(2000);

  end;

end;

procedure OpenDLLWindow(AMainHandle:Integer);stdcall;

begin

  if DLLForm=nil then

    DLLForm:=TDLLForm.Create(Application);

  // 拷挂到主程序

  //Application.Handle:=AMainHandle;

  //DLLForm.ShowModal;

  DLLForm.Show;

end;

exports OpenDLLWindow;

procedure TDLLForm.FormShow(Sender: TObject);

var

  DLLThread :TDLLThread;

begin

  DLLThread:=TDLLThread.Create(False);

end;

procedure TDLLForm.WMRefreshForm(var Msg: TMessage);

begin

  if Msg.Msg=WM_USER+100 then

  begin

    TThreadProcedure(Pointer(Msg.WParam)).Invoke;

  end;

end;

end.

如果方法TDLLThread.Execute改为

procedure TDLLThread.Execute;

var

  Count:Integer;

  tp:TThreadProcedure;

begin

  Count:=0;

  while True do

  begin

    try

      tp:= procedure

      begin

        ShowMessage(IntToStr(Count));

        Count:=Count+2000;

      end;

      SendMessage(DLLForm.Handle, WM_USER+100,Integer(@tp),0);

    except

      // ignore error

    end;

    Sleep(2000);

  end;

end;

那么TDLLForm.WMRefreshForm方法需改为:

procedure TDLLForm.WMRefreshForm(var Msg: TMessage);

begin

  if Msg.Msg=WM_USER+100 then

  begin

    TThreadProcedure(Pointer(Msg.WParam)^).Invoke;

  end;

end;

这点需要注意。

       这样一来我们在DLL程序内只需要稍加修改就可以实现Synchronize一模一样的效果,尽情使用匿名方法给我们带来的方便,以上代码在Delphi2010内通过调试,分享给大家,不足之处往指教。

                                                                             作者:张皓

                                                                                                                      2010-7-30