本文是没有写过delphi的多线程,对delphi6的线程类TThread不熟悉的人而写的,主要从 TThread的源代码入手.(其他版本的delphi,请参照此文自行理解)

 Delphi为多线程的实现专门封装了一个TThread类来实现,我们从Create函数入手来认识一下这个类,这里一般都是windows下的开发,所以先去掉linux环境的代码:

constructor TThread.Create(CreateSuspended: Boolean);
begin
  inherited Create;
  AddThread;
  FSuspended := CreateSuspended;
  FCreateSuspended := CreateSuspended;
  FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);
  if FHandle = 0 then
    raise EThread.CreateResFmt(@SThreadCreateError, [SysErrorMessage(GetLastError)]);
end;

TThread的构造函数只有一个CreateSuspended参数,该参数标示线程创建后是否挂起.

AddThread过程主要是创建一个同步时要用到的list及对线程数做一个计数.

这里主要是一个BeginThread函数的调用,BeginThread是对windows API函数CreateThread的一个封装,大家可以点进去看以下,基本没做什么操作,CreateThread的一个重要参数就是线程函数的入口地址(这里的
@ThreadProc),该函数的内容就是线程要做的真正的事(对于后面的Pointer(Self),和CREATE_SUSPENDED参数第一个是用于向线程传入参数时所要用到的指针,这里指向了self线程本身,因为在线程函数ThreadProc里并没有看到使用到它这里就不先多说了,在以后我的完成端口的例子里我会介绍如何使用它,CREATE_SUSPENDED是告诉线程创建后先挂起).我们可以点进去看下ThreadProc函数的定义

function ThreadProc(Thread: TThread): Integer;
var
  FreeThread: Boolean;
begin
  try
    if not Thread.Terminated then
    try
      Thread.Execute;
    except
      Thread.FFatalException := AcquireExceptionObject;
    end;
  finally
    FreeThread := Thread.FFreeOnTerminate;
    Result := Thread.FReturnValue;
    Thread.FFinished := True;
    Thread.DoTerminate;
    if FreeThread then Thread.Free;
    EndThread(Result);
  end;
end;

这里我们可以看到线程函数是先判断Terminated ,否的话执行Execute,最后根据FreeOnTerminate的属性决定是否释放线程.我们看Execute的定义,它是一个纯虚方法,所以用户在用继承TThread类来实现多线程时必须要覆盖Execute方法,添加代码告诉线程要做什么事.

有的人要问了,既然上面的BeginThread创建线程时是挂起的,那线程是什么时候开始工作的呢,这里就有看具体,create函数传入的参数了,如果是false表示线程创建后立即执行,具体是在

procedure TThread.AfterConstruction;
begin
  if not FCreateSuspended then
    Resume;
end;

方法里,这个方法是覆盖了TObject的虚方法,它在类创建后被delphi自动调用,我们看到它就2句代码,即如果传入的为false时即调用Resume方法,当Create指定线程创建后挂起时,用户必须自己调用此方法让线程开始执行.

TThread类提供了一个同步函数Synchronize,用于在多线程时做好同步操作,它的代码如下:

procedure TThread.Synchronize(Method: TThreadMethod);
var
  SyncProc: TSyncProc;
begin
  if GetCurrentThreadID = MainThreadID then
    Method
  else
  begin
    SyncProc.Signal := CreateEvent(nil, True, False, nil);
    try
      EnterCriticalSection(ThreadLock);
      try
        FSynchronizeException := nil;
        FMethod := Method;
        SyncProc.Thread := Self;
        SyncList.Add(@SyncProc);
        ProcPosted := True;
        if Assigned(WakeMainThread) then
          WakeMainThread(Self);
        LeaveCriticalSection(ThreadLock);
        try
          WaitForSingleObject(SyncProc.Signal, INFINITE);
        finally
          EnterCriticalSection(ThreadLock);
        end;
      finally
        LeaveCriticalSection(ThreadLock);
      end;
    finally
      CloseHandle(SyncProc.Signal);
    end;
    if Assigned(FSynchronizeException) then raise FSynchronizeException;
  end;
end;

它只有一个参数Method,我们可以看到它的定义 TThreadMethod = procedure of object;说明它是一个过程类型,需要传入一个过程的过程名.这个函数的代码看上去不难,但你如果稍微仔细点又会发现好象有点不对劲,因为除了当当前线程是主线程时会直接调用Method过程,下面并没有调用Method过程,那么它是在哪调用同步过程的呢?其实delphi的线程处理里是把同步过程的代码放到了主线程里调用,即Application所在的线程。我们从上面看到当当前线程不是主线程时,它首先创建了一个事件(SyncProc结构定义了一个事件和一个TThread对象),并将当前线程对象绑定到SyncProc结构,同时把这个结构的地址加到AddThread(Create方法里调用的第一个过程)里创建的SyncList里,后面它执行了一句这样的代码

if Assigned(WakeMainThread) then
          WakeMainThread(Self);

从定义可以看到WakeMainThread是一个事件变量,而且默认指向nil,我们在classes单元里可以搜到只有刚出现的几个地方有出现WakeMainThread,也就是说它没有在这个单元里的任何地方被赋值或调用,那么它是在哪被赋值的呢?(如果没有被赋值,那它永远会是nil,这些代码在这里就完全没有意义了,borland的那些工程师不至于犯这么低级的错误),其实通过全文搜索,你可以发现它在Forms单元里被赋值了

procedure TApplication.WakeMainThread(Sender: TObject);
begin
  PostMessage(Handle, WM_NULL, 0, 0);
end;

procedure TApplication.HookSynchronizeWakeup;
begin
  Classes.WakeMainThread := WakeMainThread;
end;
原来饶了一圈它只是要给Application发一个WM_NULL消息,而HookSynchronizeWakeup也是在Application创建时被调用了。我们再来看看Application处理WM_NULL的代码,在哪找?当然是WndProc里先找了,发现就在这里调用了,而且是调用了classes单元的CheckSynchronize方法。原来饶了一圈又回来了。

function CheckSynchronize: Boolean;
var
  SyncProc: PSyncProc;
begin
  if GetCurrentThreadID <> MainThreadID then
    raise EThread.CreateResFmt(@SCheckSynchronizeError, [GetCurrentThreadID]);
  if ProcPosted then
  begin
    EnterCriticalSection(ThreadLock);
    try
      Result := (SyncList <> nil) and (SyncList.Count > 0);
      if Result then
      begin
        while SyncList.Count > 0 do
        begin
          SyncProc := SyncList[0];
          SyncList.Delete(0);
          try
            SyncProc.Thread.FMethod;
          except
            SyncProc.Thread.FSynchronizeException := AcquireExceptionObject;
          end;
          SetEvent(SyncProc.signal);
        end;
        ProcPosted := False;
      end;
    finally
      LeaveCriticalSection(ThreadLock);
    end;
  end else Result := False;
end;

第一句不用说了,既然放在主线程里执行,那不是主线程就报错。接着判断的ProcPosted已经在前面设置为true了,接下来就是对SyncList里的所有add进来要同步执行的函数循环执行并删除,执行完一个给它一个信号(SyncProc.signal)。

另外还有一个WaitFor方法,此方法会在Destroy调用,以便在线程类释放前等待线程执行结束后再做释放操作,Destroy方法里面的代码比较简单,这里不再多说了.说说WaitFor

function TThread.WaitFor: LongWord;
var
  H: THandle;
  WaitResult: Cardinal;
  Msg: TMsg;
begin
  H := FHandle;
  if GetCurrentThreadID = MainThreadID then
  begin
    WaitResult := 0;
    repeat
      { This prevents a potential deadlock if the background thread
        does a SendMessage to the foreground thread }
      if WaitResult = WAIT_OBJECT_0 + 1 then
        PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
      Sleep(0);
      CheckSynchronize;
      WaitResult := MsgWaitForMultipleObjects(1, H, False, 0, QS_SENDMESSAGE);
      Win32Check(WaitResult <> WAIT_FAILED);
    until WaitResult = WAIT_OBJECT_0;
  end else WaitForSingleObject(H, INFINITE);
  CheckThreadError(GetExitCodeThread(H, Result));
end;

这里着重看repeat的循环里的代码,首先看下注释,意思是:这里的代码是防止一个潜在的线程死琐的可能,如后台线程向前台线程(主线程)SendMessage 一个消息时.这里可能最不理解的会时MsgWaitForMultipleObjects这个东西了,这个函数在这里的作用是,当在0毫秒的时间里H线程SendMessage 一个消息或发出一个事件信号时会得到一个WAIT_OBJECT_0或WAIT_OBJECT_0+1的返回值(具体看这个api的帮助,百度百科里有),当返回WAIT_OBJECT_0时表示函数关注的所有对象均发出信号(这里只有子线程一个对象),返回为WAIT_OBJECT_0+1时表示H线程有了一个SendMessage ,这时候要用PeekMessage取过来以防长事件的循环里出现消息死琐.(以上是本人理解,有不对的地方请大家指正.)

到此我们对TThread类对线程的封装应该有所了解了,关于线程同步要用到信号灯,事件,临界区,互斥量等我会在下一篇里做个简要的介绍.

认识Delphi的线程类相关推荐

  1. Delphi的线程类

    本文是没有写过delphi的多线程,对delphi的线程类TThread不熟悉的人而写的,主要从 TThread的源代码入手. Delphi为多线程的实现专门封装了一个TThread类来实现,我们从C ...

  2. Delphi中线程类TThread实现多线程编程2---事件、临界区、Synchronize、WaitFor……

    接着上文介绍TThread. 现在开始说明 Synchronize和WaitFor 但是在介绍这两个函数之前,需要先介绍另外两个线程同步技术:事件和临界区 事件(Event) 事件(Event)与De ...

  3. Delphi中的线程类

    Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchr ...

  4. Delphi线程类的使用(1)

    Delphi线程类的使用 猛禽[Mental Studio](个人专栏)(BLOG) http://mental.mentsu.com 去年底我写过一篇文章<Delphi中的线程类.2.3.4. ...

  5. Delphi中的线程类--之(1)

    Delphi中的线程类 猛禽[Mental Studio] http://mental.mentsu.com ( 之一) Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数D ...

  6. Delphi中的线程类--之(2)

    Delphi中的线程类 猛禽[Mental Studio] http://mental.mentsu.com 之二 首先就是构造函数: constructor TThread.Create(Creat ...

  7. Delphi中的线程类Thread

    原文:http://www.heibai.net/article/info/info.php?infoid=22594 Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数De ...

  8. DELPHI 线程类

    转自http://www.cnblogs.com/chengxin1982/archive/2009/10/04/1577879.html Delphi中有一个线程类TThread是用来实现多线程编程 ...

  9. delphi多线程TThread类介绍

    Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchr ...

最新文章

  1. CoreCLR系列随笔 之ClrJit项目之alloc.cpp文件分析(1)
  2. HDLBits 系列(7)对for循环以及generate for的各种实践
  3. DPDK vhost-user之mergeable 特性(七)
  4. 产品汪们匿名自爆工资,看完心里好难受......
  5. IO中的阻塞、非阻塞、同步、异步概念分析详解
  6. SAP UI5 应用开发教程之五十一 - 如何使用 Chrome 调试运行在手机上的 SAP UI5 Cordova 混合应用
  7. 四因素三水平正交试验表_测试用例设计方法(二)——正交实验法
  8. 将CSV文件存为HTML文件形式
  9. Windows7 x64在Wampserver上安装memcache
  10. 十大抽奖软件推荐 抽奖软件排行榜 在线抽奖软件有哪些
  11. Axure 9.0 使用教程2-函数分类
  12. 中英文自动翻译(有道翻译、彩云小译)
  13. 使用 kind 1 分钟启动一个本地 k8s 开发集群
  14. 一对一营销与传统营销的区别在哪里?企业如何开展营销?
  15. 激励函数简介 Tensorflow最简单的三层神经网络及matplotlib可视化 附激励函数常见类型
  16. Opensea到底是怎么交易NFT的
  17. Origin 在已画图中添加新的数据曲线
  18. 基于国产FPGA 的MIPI硬核应用
  19. 好用的chrome浏览器网页翻译插件
  20. 私企“跳槽率”高的四大原因

热门文章

  1. C#使用Microsoft.office.interop.PowerPoint生成PPT
  2. C#/VB.NET 在Excel单元格中应用多种字体格式
  3. AI2(App Inventor 2)离线版服务器网络版(AI伴侣2.47版)
  4. 【Android】技术调研:用代码模拟屏幕点击、触摸事件
  5. 格式化硬盘并安装Win10和Ubuntu双系统
  6. 吴恩达机器学习(十五)—— 应用实例:图片文字识别
  7. A股全市场个股涨停板明细来袭!—股票数据远程下载服务升级
  8. (day16)媒体查询和响应式布局
  9. 从零开始学微信小程序开发:1
  10. 软件企业认定条件政策