Delphi 7的线程工作方式的一些心得

来源:互联网 发布:金融炼金术 知乎 编辑:程序博客网 时间:2024/05/20 13:15

最近因为用到了Delphi 7的线程,有些地方以前没有注意到,研究了一下源代码,有一些心得体会,因为从网上学了很多东西,所以也像分享一下,虽然很浅薄,对高手来说不值一提,希望对新手能有点用处。


Delphi的帮助,其实写的很一般,但是如果有兴趣看看源代码,很多东西就明白了,这是我以前没大注意的事情。虽然现在网上信息很多,但是有时候想找到自己想要的信息很难,还不如自己看源代码。


1.、在线程里面出错,居然没有抛出异常,而是直接结束了线程!  网上看了些文章,看来是需要自己处理线程的异常。最好的办法是在线程方法里面,用一个大的try except end套起来,防止线程出错的时候,主线程一点都不知道。

文章提出来的办法,是在TApplication的异常处理里面,加上线程的异常,有空试试


2、由于Delphi的对象模式,线程即使是FreeOnTerminate设置成true,线程执行完毕,也无法检测线程是否结束了。试过检测ThreadID等方式,都无法检测。而且对象销毁以后,也是无法检测对象是否还存在的,甚至对象的方法还能执行。唯一的办法,是手工把这个线程对象设置成nil。


3、线程同步的工作原理

因为VCL不是线程安全的,所以在线程里面修改主线程的界面元素,需要使用Synchronize方法。这个方法的执行原理,研究了一下,如下:

Synchronize函数原理分析:
先创建一个信号量(临时变量SyncProc.Signal), 然后进入EnterCriticalSection, 为全局变量SyncList创建列表(如果此变量没有初始化的话),然后在SyncList列表中增加一个参数为PSynchronizeRecord类型的变量(此变量中,保存了要进行同步的方法, 异常处理函数等等信息), 然后设置全局变量SyncEvent信号量为激活状态, 然后运行 WakeMainThread(SyncProc.SyncRec.FThread), 此函数在TApplication中定义, 代码如下:

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

看来其实就是发送了一个空消息WM_NULL(在主窗体的消息循环中, 遇到WM_NULL, 就会调用CheckSynchronize), 然后LeaveCriticalSection.

然后就等待这个最初创建的那个信号量SyncProc.Signal,  等待到以后, 再EnterCriticalSection, 再LeaveCriticalSection, 然后关闭这个信号量.

最后如果定义了同步异常处理对象, 就激活这个异常处理.

CheckSynchronize函数(Classes.pas文件), 检查SyncList队列(先保存此队列到局部变量中,然后销毁此队列),逐个执行其中的FMethod方法, 执行完成的时候, 激活此队列中此记录的信号量(就是上面创建的那个信号量).

因为CheckSynchronize是在消息循环中调用的,所以不会和主线程中的VCL操作同步进行。


下面是Synchroniz的源代码(只保留了Window部分,Linux的部分去掉了)

  if GetCurrentThreadID = MainThreadID then
    ASyncRec.FMethod
  else
  begin
    SyncProc.Signal := CreateEvent(nil, True, False, nil);
    try
      EnterCriticalSection(ThreadLock);
      try
        if SyncList = nil then
          SyncList := TList.Create;
        SyncProc.SyncRec := ASyncRec;
        SyncList.Add(@SyncProc);
        SignalSyncEvent;
        if Assigned(WakeMainThread) then
          WakeMainThread(SyncProc.SyncRec.FThread);
        LeaveCriticalSection(ThreadLock);
        try
          WaitForSingleObject(SyncProc.Signal, INFINITE);
        finally
          EnterCriticalSection(ThreadLock);
        end;
      finally
        LeaveCriticalSection(ThreadLock);
      end;
    finally
      CloseHandle(SyncProc.Signal);
    end;
    if Assigned(ASyncRec.FSynchronizeException) then raise ASyncRec.FSynchronizeException;
  end;



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



原创粉丝点击