多线程中触发的事件函数在哪个线程中执行的测试

 在多线程开发中,如果在多线程中访问主线程创建的对象,并触发了这个对象的事件,将会执行这个事件的处理函数,那么这个处理函数是在主线程中执行还是在触发事件的线程中执行呢?针对这个问题做了一下测试,如果没有通过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

原文地址:https://www.cnblogs.com/findumars/p/5289375.html