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


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

  • 相关阅读:
    Hibernate操作指南-实体之间的关联关系(基于注解)
    Hibernate操作指南-实体与常用类型的映射以及基本的增删改查(基于注解)
    Hibernate操作指南-搭建一个简单的示例(基于Java Persistence API JPA)
    Hibernate操作指南-搭建一个简单的示例(基于原生API和注解)
    Hibernate操作指南-搭建一个简单的示例(基于原生API和XML)
    SpringSecurity——基于Spring、SpringMVC和MyBatis自定义SpringSecurity权限认证规则
    JUnit操作指南-批量执行单元测试(将多个测试类捆绑在一起执行)
    SpringSecurity操作指南-在SpringMVC项目上配置Spring Security
    SpringMVC——Servlet容器启动时初始化SpringMVC应用的原理
    SpringMVC操作指南-整合Spring、SpringMVC、MyBatis、Log4j/Log4j2及JUnit4搭建项目框架
  • 原文地址:https://www.cnblogs.com/findumars/p/5289375.html
Copyright © 2020-2023  润新知