DLL内线程同步主线程研究(子线程代码放到主线程执行)
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
轉載于:https://www.cnblogs.com/zhmore/archive/2010/07/29/1787474.html
總結
以上是生活随笔為你收集整理的DLL内线程同步主线程研究(子线程代码放到主线程执行)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Colored Sticks--POJ
- 下一篇: js滚动效果