您的位置:首页 > 编程语言 > Delphi

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

2013-04-30 22:01 357 查看
最近因为用到了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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: