在多線程開發中,如果在多線程中訪問主線程創建的對象,並觸發了這個對象的事件,將會執行這個事件的處理函數,那么這個處理函數是在主線程中執行還是在觸發事件的線程中執行呢?針對這個問題做了一下測試,如果沒有通過Windows消息觸發事件,則在子線程(觸發事件的線程)中執行事件處理函數,如果是由Windows消息觸發的事件,則由主線程執行事件處理函數.這是因為Windows消息只由創建控件的線程進行處理,那么由此引起的事件及其處理函數自然就在創建控件的線程中執行了.而普通的事件觸發,則全部在子線程中完成的,因此在子線程中執行事件處理函數.由此也解釋了對於需要執行大量的任務的子線程,如果需要主線程顯示處理進度,則可以在子線程中直接修改進度條控件的當前位置,主線程負責處理界面顯示.這是因為子線程在修改進度條控件的當前位置時,會將一個Windows消息投遞到消息隊列,進度條的創建線程(主線程)在處理這個消息的時候,刷新界面上的進度.那么如果要在子線程中創建一個控件,並處理其由Windows消息觸發的事件,消息會有子線程處理,子線程的消息隊列會管理在本線程中創建的控件的消息.
驗證1:普通的事件(無Windows消息觸發)處理函數由子線程執行.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TNotify = procedure of object;
TCtrl = class
private
FNotify: TNotify;
public
property Notify: TNotify read FNotify write FNotify;
procedure TriggerNotify;
end;
TThrd = class(TThread)
private
FCtrl: TCtrl;
protected
procedure Execute; override;
public
constructor Create(ACtrl: TCtrl); reintroduce;
end;
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
FCtrl: TCtrl;
procedure Notify;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
OutputDebugString(PChar(Format('>>>>>:%d', [GetCurrentThreadId])));
FCtrl.TriggerNotify;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
OutputDebugString(PChar(Format('觸發多線程的線程:%d', [GetCurrentThreadId])));
TThrd.Create(FCtrl);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FCtrl := TCtrl.Create;
FCtrl.Notify := Self.Notify;
end;
{ TCtrl }
procedure TCtrl.TriggerNotify;
begin
if Assigned(FNotify) then
FNotify;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FCtrl.Free;
end;
procedure TForm1.Notify;
begin
OutputDebugString(PChar(Format('>>>>>:%d', [GetCurrentThreadId])));
end;
{ TThrd }
constructor TThrd.Create(ACtrl: TCtrl);
begin
inherited Create(False);
FCtrl := ACtrl;
end;
procedure TThrd.Execute;
begin
FCtrl.TriggerNotify;
end;
end.
驗證2:進度條處理當前位置變化的代碼中是通過Windows消息通知創建控件的線程(主線程)的
procedure TProgressBar.SetPosition(Value: Integer);
begin
if not F32BitMode and ((Value < 0) or (Value > Limit16)) then
ProgressLimitError;
if HandleAllocated then SendMessage(Handle, PBM_SETPOS, Value, 0)
else FPosition := Value;
end;
驗證3:可以在多線程中觸發一個主線程創建的控件的事件,在處理函數中輸出處理線程的ID,比較發現處理線程正是主線程.
驗證4:在子線程中創建控件並處理由Windows消息觸發的事件,事件由子線程執行.結論:子線程觸發的控件事件,處理函數由創建控件的那個線程來執行.
結論:當一個線程第一次被創建時,系統假定線程不會用於任何與用戶相關的任務.這樣可以減少線程對系統資源的要求.但是,一旦該線程調用一個與圖形用戶界面有關的函數 ( 如檢查它的消息隊列或建立一個窗口 ),系統就會為該線程分配一些另外的資源,以便它能夠執行與用戶界面有關的任務.特別是,系統分配了一個THREADINFO結構,並將這個數據結構與線程聯系起來.
另外管理控件與創建線程的關系是由Windows來完成的,將線程ID和控件的Handle進行映射,當需要向某個Handle發送消息時,會搜索其創建線程,並將消息投遞到對應線程的消息隊列中.
http://blog.csdn.net/henreash/article/details/7670420