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

推荐订阅源

有赞技术团队
有赞技术团队
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
P
Palo Alto Networks Blog
C
Cisco Blogs
The Hacker News
The Hacker News
T
Threatpost
S
Schneier on Security
K
Kaspersky official blog
Spread Privacy
Spread Privacy
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
NISL@THU
NISL@THU
量子位
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google DeepMind News
Google DeepMind News
Security Latest
Security Latest
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
博客园 - 叶小钗
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
News and Events Feed by Topic
爱范儿
爱范儿
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
Project Zero
Project Zero
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Cisco Talos Blog
Cisco Talos Blog
GbyAI
GbyAI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Apple Machine Learning Research
Apple Machine Learning Research
T
Tenable Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Vulnerabilities – Threatpost
Forbes - Security
Forbes - Security
博客园 - 三生石上(FineUI控件)
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News and Events Feed by Topic
V
V2EX
Webroot Blog
Webroot Blog
The Register - Security
The Register - Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Blog — PlanetScale
Blog — PlanetScale
M
MIT News - Artificial intelligence
Scott Helme
Scott Helme
Simon Willison's Weblog
Simon Willison's Weblog
L
LangChain Blog
W
WeLiveSecurity
Cloudbric
Cloudbric

博客园 - 张皓

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