Delphi多线程的OnTerminate属性(附加一个关于临界区线程同步的例子)

  首先看TThread源码中关于OnTerminate的代码:

public
    ....
    property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
    ...
end;

  再看Delphi自带的帮助手册中对于OnTerminate的解释:

Occurs after the thread‘s Execute method has returned and before the thread is destroyed.

property OnTerminate: TNotifyEvent;

Description

Write an OnTerminate event handler to execute code after the thread finishes executing. The OnTerminate event handler is called in the context of the main thread, which means CLX methods and properties can be called freely.

  翻译成中文的大致的解释就是:线程的该属性如果被赋值为一个方法,那么此方法将会在该线程的Execute方法执行完成并退出,但是线程还没有被释放之前执行。

  一个实际的例子:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
  private
    procedure ThreadDone(Sender: TObject);
  public
    { Public declarations }
  end;

  TTestThread = class(TThread)
  protected
    procedure Execute; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
  MaxSize = 128;
var
  NextNumber: Integer = 0;
  DoneFlags: Integer = 0;
  GlobalArray: array[1..MaxSize] of Integer;
  CS: TRTLCriticalSection;

  function GetNextNumber: Integer;
  begin
    Result:= NextNumber;
    Inc(NextNumber);
  end;

  procedure TTestThread.Execute;
  var
    i: Integer;
  begin
    OnTerminate:= Form1.ThreadDone;    //在这里设置OnTerminate属性为Form1的ThreadDone方法,表示在线程执行完Execute之后,还没有被释放之前,要紧接着执行Form1的ThreadDone方法。
    EnterCriticalSection(CS);
    for i:= 1 to MaxSize do
    begin
      GlobalArray[i]:= GetNextNumber;
      Sleep(5);
    end;
    LeaveCriticalSection(CS);
  end;

  procedure TForm1.ThreadDone(Sender: TObject);
  var
    i: Integer;
  begin
    inc(DoneFlags);
    if DoneFlags = 2 then
    begin
      for i:= 1 to MaxSize do
        lst1.Items.Add(IntToStr(GlobalArray[i]));
      DeleteCriticalSection(CS);
    end;
  end;

procedure TForm1.btn1Click(Sender: TObject);
begin
  initializeCriticalSection(CS);
  TTestThread.Create(False);
  TTestThread.Create(False);

end;

end.

  先解释OnTerminate:在这里设置OnTerminate属性为Form1的ThreadDone方法,表示在线程执行完Execute之后,还没有被释放之前,要紧接着执行Form1的ThreadDone方法。

  再对线程的同步进行说明:这里使用临界区进行线程的同步,该例程会在点击按钮之后开启两个线程。

               每个线程都是对一个全局数组GlobalArray进行操作,使用了临界区的话,(看TForm1的btn1Click方法)虽然是连续开启两个线程,但是因为使用了临界区,而且是在线程的Execute中进行操作之前先进入临界区,所以当一个线程运行(也就是执行Execute方法)时,第一个线程进入临界区对全局数组GlobalArray进行性操作,那么另外一个线程就只能等待,等到第一个线程完成操作并且离开临界区之后(此时GlobalArray数组的值是从0到127)。第二个线程开始进入临界区,并且开始对GlobalArray进行操作,最后GlobalArray数组中的值是从128到255。

               通过使用临界区就可以保证多个线程不能同时对一个资源进行操作,而如果没有使用临界区对线程进行同步的话,那么最后可能是线程1、线程2随机连续对数组进行操作(因为操作系统对线程的时间片的分配是随机的)(还有NextNumber这个全局变量也会被两个线程随机连续操作,可能造成冲突),最后的结果就是当两个线程都执行完成之后,GlobalArray数组中的值没有任何规律,所以这时候就不可能根据规则来控制线程,这时候开发出来的程序就是开发者所不能控制的,显然不利于程序的稳定性等。

               所以在多个线程会去操作同一个资源等情况的时候,必须对线程进行同步,保证线程的可控性。

时间: 2024-10-12 07:53:46

Delphi多线程的OnTerminate属性(附加一个关于临界区线程同步的例子)的相关文章

Delphi多线程编程之同步读写全局数据

开始研究最重要的多线程读写全局数据了,结合书上的例子,我修改成下面的情况: unit Tst_Thread3U; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls; type   TForm1 = class(TForm)     Button1: TButton;     Memo1: TMemo;     Button2: TBu

delphi 多线程2

多线程常用的内核对象:CreateEvent事件,CreateMutex互斥,CreateSemaphore信号,CreateWaitableTimer计时器 {建立事件} function CreateEvent( lpEventAttributes: PSecurityAttributes; {!} bManualReset: BOOL; bInitialState: BOOL; lpName: PWideChar ): THandle; stdcall; {建立互斥} function C

教程-Delphi中Spcomm使用属性及用法详解

Delphi中Spcomm使用属性及用法详解 Delphi是一种具有 功能强大.简便易用和代码执行速度快等优点的可视化快速应用开发工具,它在构架企业信息系统方面发挥着越来越重要的作用,许多程序员愿意选择 Delphi作为开发工具编制各种应用程序.但是,美中不足之处是 Delphi没有自带的串口通信控件,在它的帮助文档里也没有提及串口通信,这就给编制通信程序的开发人员带来许多不便. 目前,利用 Delphi实现串口通信的常用的方法有 3种:一是利用控件,如 MSCOMM控件和 SPCOMM控件:二

delphi 多线程编程

开始本应该是一篇洋洋洒洒的文字, 不过我还是提倡先做起来, 在尝试中去理解.先试试这个: procedure TForm1.Button1Click(Sender: TObject); var i: Integer; begin for i := 0 to 500000 do begin Canvas.TextOut(10, 10, IntToStr(i)); end; end; 上面程序运行时, 我们的窗体基本是 "死" 的, 可以在你在程序运行期间拖动窗体试试... Delphi

提示13. 附加一个实体的简单方式

提示13. 附加一个实体的简单方式 问题: 在早先的一些提示中,我们讨论了使用Attach来加载一个处于未改变(unchanged)状态的东西到ObjectContext从而避免进行查询的开销. 如果性能是你的目标,Attach就是要选择的武器. 不幸的是我们的API不能适应99%的情况,即每个类型仅有一个实体集(entity set)的情况.Entity Framework支持单类型多实体集(Multiple Entity Sets perType)或称MEST,且API反映了这一点,要求你提

Delphi多线程下的ADO编程

前言: 几个月前接到一个任务:将一后台程序访问数据库的方式从BDE改为ADO,原因是由于业务量的增加,通过BDE不论是向数据库写入数据还是从数据库中读出数据的速度都变得无法忍受,大家都知道ADO在数据库访问速度方面比BDE要快的多了(我写了一个测试程序使用ADO比使用BDE快了近100倍!).这个任务还不简单嘛,只要将BDE的控件更换成ADO的再修改一些代码不就搞定了!我当时确实是这么想的,而且用了不到一个小时就搞定,测试运行一段没问题,大功告成了,我想.谁知道一个恶梦就此开始,我的愚昧无知使我

转:学习笔记:delphi多线程学识

学习笔记:delphi多线程知识 最近一直在温习旧的知识,刚好学习了一下Java的线程安全方面的知识,今天想起之前一直做的Delphi开发,所以还是有必要温习一下,看看这些不同的编程语言有什么不同之处. Delphi的线程同步方法: 1.临界区 申明一个临界资源 FLock : TRTLCriticalSection; 先初化一个临界资源对象 InitializeCriticalSection(FLock) 销毁临界资源对象 DeleteCriticalSection(FLock) proced

Delphi中线程类TThread实现多线程编程(线程同步技术、Synchronize、WaitFor……)

接着上文介绍TThread. 现在开始说明 Synchronize和WaitFor 但是在介绍这两个函数之前,需要先介绍另外两个线程同步技术:事件和临界区 事件(Event)与Delphi中的事件有所不同.从本质上讲,Event其实就相当于一个全局的布尔变量.它有两个赋值操作:Set和ReSet,相当于把它设置为 True或False.而检查它的值是通过WaitFor操作进行.对应在Windows平台上,是三个API函数:SetEvent.ResetEvent.WaitForSignalObje

Delphi多线程编程(12)--多线程同步之Semaphore(信号对象)

转载自:万一的博客 之前已经有了两种线程同步的方法: CriticalSection(临界区)和Mutex(互斥)吗,这两种同步方法差不多,只是作用域不同 CriticalSection类似于只有一个蹲位的公共厕所,只能一个个地进 Mutex 对象类似于接力赛中的接力棒,某一时刻只能有一个人持有,谁拿着谁跑 什么是Semaphore(信号或叫信号量)呢? 譬如到银行办业务.或者到车站买票,原来只有一个服务员,不管有多少人排队等候,业务只能一个个地来 假如增加业务窗口,可以同时受理几个业务呢? 这