Delphi多线程编程(1)--先入门再说

转载:万一的博客

  多线程应该是编程工作者的基础技能, 但这个基础对我来讲的确有点难(起码昨天以前是这样)。

  开始本应该是一篇洋洋洒洒的文字, 不过我还是提倡先做起来, 在尝试中去理解。



  先试试这个

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为我们提供了一个简单的方法(Application.ProcessMessages)来解决这个问题

procedure TForm1.Button1Click(Sender: TObject);
var
    i: Integer;
begin
    for i:= 0 to 500000 do
    begin
        Canvas.TextOut(10, 10, IntToStr(i));
        Application.ProcessMessages;
    end;
end;

  这个Application.ProcessMessages 一般用在比较费时的循环中,它会检查并处理消息队列中的其他消息

  但是这算不上多线程,譬如:运行中你拖动窗体,循环会暂停下来



  在使用多线程以前,让我们先简单修改一下程序

function MyFun: Integer;
var
    i: Integer;
begin
    for i:= 0 to 500000 do
    begin
        Form1.Canvas.Lock;
        Form1.Canvas.TextOut(10, 10, IntToStr(i));
        Form1.UnLock;
    end;
    Result:= 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
    MyFun;
end;

  细数上面程序的变化:

1. 首先这还不是多线程的,也会让窗体假“死”一会

2. 把执行代码写在一个函数中,但这个函数不属于 TForm1的方法,所以使用Canvas是必须冠以名称(Form1)

3. 既然是个函数,(不管是否必要)都应该有返回值

4. 使用了 5000001 次Lock和UnLock

  Canvas.Lock好比在说:Canvas(绘图表面)正在忙着呢,其他想用Canvas的等会

  Canvas.UnLock:用完了,解锁

  在Canvas中使用 Lock和 UnLock是一个好习惯,在不使用多线程的情况下这是无所谓的,但是保不准哪天程序会扩展为多线程的。我们现在学习多线程,当然应该用



  在Delphi中使用多线程有两种方法:调用API、使用 TThread类。

  使用API 的代码更简单

function MyFun(P: Pointer): Integer; stdcall;
var
    i: Integer;
begin
    for i:=0 to 50000 do
    begin
        Form1.Canvas.Lock;
        Form1.Canvas.TextOut(10, 10, IntToStr(i));
        Form1.Canvas.UnLock;
    end;
    Result:= 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
    ID: THandle;
begin
    CreateThread(nil, 0, @MyFun, nil, 0, ID);
end;

  代码分析:

  CreateThread 一个线程之后,算上原来的主线程,这样程序就有两个线程,是标准的多线程了

  CreateThread 的第三个参数是函数指针,新线程建立后将立即执行该函数,函数执行完毕,系统将销毁此线程从而结束多线程的故事

  CreateThread  要使用的函数是系统级别的,不能是某个类(譬如:TForm1)的方法,并且有严格的格式(参数、返回值)要求,不管你暂时是不是需要都必须按格式来

  因为是系统级调用,还要缀上stdcall,stdcall是协调参数顺序的,虽然这里只有一个参数没有顺序可言,但这是使用系统函数的惯例

  CreateThread 还需要一个var 参数来接收新建线程的ID,尽管暂时没用,但这也是格式;爱他参数以后再说吧



  这样一个最简单的多线程程序就出来了,咱们再用 TThread类实现一次

type
    TMyThread=class(TThread)
    protected
        procedure Execute; override;
    end;

procedure TMyThread.Execute;
var
    i: Integer;
begin
    FreeOnTerminate:= True;    //这可以让线程执行完毕之后随即释放
    for i:= 0 to 500000 do
    begin
        Form1.Canvas.Lock;
        Form1.Canvas.TextOut(10, 10, IntToStr(i));
        Form1.Canvas,UnLock;
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
    TMyThread.Create(False);
end;

  TThread 类有一个抽象方法(Execute),因而是一个抽象类,抽象类只能继承使用,上面是继承为 TMyThread

  继承 TThread主要就是实现抽象方法 Execute(把我们的代码写到里面),等我们的TMyThread实例化之后,首先就会执行 Execute方法中的代码

  按常规我们一般会这样去实例化

procedure TForm1.Button1Click(Sender: TObject);
var
    MyThread: TMyThread;
begin
    MyThread:= TMyThread.Create(False);
end;

  因为 MyThread变量在这里毫无用处(并且编译器还有提示),所以不如直接写做TMyThread.Create(False);



  我们还可以轻松解决一个问题,如果:TMyThread.Create(True)?

  这样线程创建之后就不会立即调用Execute,可以在需要的时候再用 Resume方法执行线程,譬如

procedure TForm1.Button1Click(Sender: TObject);
var
    MyThread: TMyThread;
begin
    MyThread:= TMyThread.Create(True);
    MyThread.Resume;
end;

  可简化为

procedure TForm1.Button1Click(Sender: TObject) ;
begin
    with TMyThread.Create(True) do Resume;
end;

  使用 TThread 类时, Delphi 有提供的模板, 使用 IDE 写代码很方便, 我重写一遍录下来给大家看:

  期间使用了 Ctrl+J、 Shift+Ctrl+C、Ctrl+Alt+P



  重要的修正和补充

  在TThread 类的例子中,应该有这句 FreeOnTerminate:= True;(原来漏掉,代码中已加上,但动画加不上了)

  先说它是什么意思:

  类 Create了就要 Free

  但是TThread(子类)有特殊性,很多时候我们不能确定新建的线程什么时候执行完(也就是什么时候该释放)

  如果线程执行完毕自己知道释放就好了,所以 TThread给了一个布尔属性 FreeOnTerminate,如果为 True,线程执行完毕后会自动释放



  我怎么会忽略了这么重要的问题呢? 原因有二: 
1、我一直在追求最精炼的代码;
2、我手头上不只一本书上介绍说: FreeOnTerminate 的默认值是 True(错误!), 经落实, 应该是 False, 起码在 Delphi 2007 和 2009 中是这样; 或许以前的某个版本和现在不一样.(不过有些博客和书中说默认值是True,但是不纠结这个问题了,如果以后需要用,无论是想要用 True还是False,还是都自己显式的赋值吧,这就不会存在上面的问题了)

时间: 2024-11-08 22:42:56

Delphi多线程编程(1)--先入门再说的相关文章

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

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多线程编程(8)--多线程同步之CriticalSection(临界区)

转载自:万一的博客 先看一段程序 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1=class(TForm) ListBox1: TListBox; Button1: TButton; procedure FormCreate(Sender: TObject); procedure

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

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

python多线程编程—同步原语入门(锁Lock、信号量(Bounded)Semaphore)

摘录python核心编程 一般的,多线程代码中,总有一些特定的函数或者代码块不希望(或不应该)被多个线程同时执行(比如两个线程运行的顺序发生变化,就可能造成代码的执行轨迹或者行为不相同,或者产生不一致的数据),比如修改数据库.更新文件或其他会产生竞态条件的类似情况.此时就需要同步了. 同步:任意数量的线程可以访问临界区的代码,但在给定的时刻又只有一个线程可以通过时. 这里介绍两个基本的同步类型原语:锁/互斥.信号量 锁 锁有两种状态:锁定和未锁定.与之对应的是两个函数:获得锁和释放锁. 当多线程

Delphi多线程编程(9)--认识等待函数WaitForSingleObject

转载自:万一的博客 一下子跳到等待函数 WaitForSingleObject,是因为下面的Mutex.Semaphore.Event.WaitableTimer等同步手段都要使用这个函数.不过等待函数可不止WaitForSingleObject这一个,但是它是最简单的 function WaitForSingleObject( hHandle: THandle; //要等待的对象句柄 dwMillseconds: DWORD; //等待的时间,单位是毫秒 ): DWORD; srdcall;

Delphi多线程编程(10)--多线程同步之Mutex(互斥对象)

原理分析: 互斥对象是系统内核对象,各个线程都可以拥有它,谁拥有它谁就可以执行 执行完毕,用ReleaseMutex 函数释放拥有权,以让其他等待的线程可以使用 其他线程可以使用 WaitForSingleObject函数排队等待(等待也可以理解为排队申请) 使用过程 var hMutex: THandle; {应该先声明一个全局的互斥句柄} CreateMutex {建立互斥对象} WaitForSingleObject {用等待函数排队等候} ReleaseMutex {释放拥有权} Clo

Delphi多线程编程(15)--多线程同步之WaitableTimer(等待定时器对象)[续]

转载自:万一的博客 本次专门研究下 SetWaitableTimer的第二个参数(起始时间) 它有正值.负值.0值这三种情况,前面已经用过 0 值 先学习负值(相对时间),也就是从当前算起隔多长时间开始执行 这个相对时间是以 1/100 纳秒为单位的,譬如赋值:3*10000000 相当于 3 秒 1 s(秒) = 1,000            ms(毫秒); 1 s(秒) = 1,000,000        µs(微妙); 1 s(秒) = 1,000,000,000    ns(纳秒)

Delphi多线程编程(14)--多线程同步之WaitableTimer(等待定时器对象)

转载自:万一的博客 function CreateWaitableTimer( lpTimerAttributes: PSecurityAttributes; //安全 bManualReset: BOOL; //True:可调度多个线程:False:只调度一个线程 lpTimerName: PWideChar //名称 ):THandle; stdcall; //返回句柄 function SetWaitableTimer( hTime: THandle; //句柄 var lpDueTime