程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi中的線程類(2)

Delphi中的線程類(2)

編輯:Delphi

首先就是構造函數:

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;

雖然這個構造函數沒有多少代碼,但卻可以算是最重要的一個成員,因為線程就是在這裡被創建的。

在通過Inherited調用TObject.Create後,第一句就是調用一個過程: AddThread,其源碼如下:

procedure AddThread;
begin
  InterlockedIncrement(ThreadCount);
end;

同樣有一個對應的RemoveThread:

procedure RemoveThread;
begin
  InterlockedDecrement(ThreadCount);
end;

它們的功能很簡單,就是通過增減一個全局變量來統計進程中的線程數。只 是這裡用於增減變量的並不是常用的Inc/Dec過程,而是用了 InterlockedIncrement/InterlockedDecrement這一對過程,它們實現的功能完 全一樣,都是對變量加一或減一。但它們有一個最大的區別,那就是 InterlockedIncrement/InterlockedDecrement是線程安全的。即它們在多線程 下能保證執行結果正確,而Inc/Dec不能。或者按操作系統理論中的術語來說, 這是一對“原語”操作。

以加一為例來說明二者實現細節上的不同:

一般來說,對內存數據加一的操作分解以後有三個步驟:

1、從內存中讀出數據

2、數據加一

3、存入內存

現在假設在一個兩個線程的應用中用Inc進行加一操作可能出現的一種情況:

1、線程A從內存中讀出數據(假設為3)

2、線程B從內存中讀出數據(也是3)

3、線程A對數據加一(現在是4)

4、線程B對數據加一(現在也是4)

5、線程A將數據存入內存(現在內存中的數據是4)

6、線程B也將數據存入內存(現在內存中的數據還是4,但兩個線程都對它加 了一,應該是5才對,所以這裡出現了錯誤的結果)

而用InterlockIncrement過程則沒有這個問題,因為所謂“原語”是一種不 可中斷的操作,即操作系統能保證在一個“原語”執行完畢前不會進行線程切換 。所以在上面那個例子中,只有當線程A執行完將數據存入內存後,線程B才可以 開始從中取數並進行加一操作,這樣就保證了即使是在多線程情況下,結果也一 定會是正確的。

前面那個例子也說明一種“線程訪問沖突”的情況,這也就是為什麼線程之 間需要“同步”(Synchronize),關於這個,在後面說到同步時還會再詳細討 論。

說到同步,有一個題外話:加拿大滑鐵盧大學的教授李明曾就Synchronize一 詞在“線程同步”中被譯作“同步”提出過異議,個人認為他說的其實很有道理 。在中文中“同步”的意思是“同時發生”,而“線程同步”目的就是避免這種 “同時發生”的事情。而在英文中,Synchronize的意思有兩個:一個是傳統意 義上的同步(To occur at the same time),另一個是“協調一致”(To operate in unison)。在“線程同步”中的Synchronize一詞應該是指後面一種 意思,即“保證多個線程在訪問同一數據時,保持協調一致,避免出錯”。不過 像這樣譯得不准的詞在IT業還有很多,既然已經是約定俗成了,本文也將繼續沿 用,只是在這裡說明一下,因為軟件開發是一項細致的工作,該弄清楚的,絕不 能含糊。

扯遠了,回到TThread的構造函數上,接下來最重要就是這句了:

FHandle := BeginThread(nil, 0, @ThreadProc, Pointer(Self), CREATE_SUSPENDED, FThreadID);

這裡就用到了前面說到的Delphi RTL函數BeginThread,它有很多參數,關鍵 的是第三、四兩個參數。第三個參數就是前面說到的線程函數,即在線程中執行 的代碼部分。第四個參數則是傳遞給線程函數的參數,在這裡就是創建的線程對 象(即Self)。其它的參數中,第五個是用於設置線程在創建後即掛起,不立即 執行(啟動線程的工作是在AfterConstruction中根據CreateSuspended標志來決 定的),第六個是返回線程ID。

現在來看TThread的核心:線程函數ThreadProc。有意思的是這個線程類的核 心卻不是線程的成員,而是一個全局函數(因為BeginThread過程的參數約定只 能用全局函數)。下面是它的代碼:

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.DoTerminate;
   Thread.FFinished := True;
   SignalSyncEvent;
   if FreeThread then Thread.Free;
   EndThread(Result);
  end;
end;

雖然也沒有多少代碼,但卻是整個TThread中最重要的部分,因為這段代碼是 真正在線程中執行的代碼。下面對代碼作逐行說明:

首先判斷線程類的Terminated標志,如果未被標志為終止,則調用線程類的 Execute方法執行線程代碼,因為TThread是抽象類,Execute方法是抽象方法, 所以本質上是執行派生類中的Execute代碼。

所以說,Execute就是線程類中的線程函數,所有在Execute中的代碼都需要 當作線程代碼來考慮,如防止訪問沖突等。

如果Execute發生異常,則通過AcquireExceptionObject取得異常對象,並存 入線程類的FFatalException成員中。

最後是線程結束前做的一些收尾工作。局部變量FreeThread記錄了線程類的 FreeOnTerminated屬性的設置,然後將線程返回值設置為線程類的返回值屬性的 值。然後執行線程類的DoTerminate方法。

DoTerminate方法的代碼如下:

procedure TThread.DoTerminate;
begin
  if Assigned(FOnTerminate) then Synchronize(CallOnTerminate);
end;

很簡單,就是通過Synchronize來調用CallOnTerminate方法,而 CallOnTerminate方法的代碼如下,就是簡單地調用OnTerminate事件:

procedure TThread.CallOnTerminate;
begin
  if Assigned(FOnTerminate) then FOnTerminate(Self);
end;

因為OnTerminate事件是在Synchronize中執行的,所以本質上它並不是線程 代碼,而是主線程代碼(具體見後面對Synchronize的分析)。

執行完OnTerminate後,將線程類的FFinished標志設置為True。

接下來執行SignalSyncEvent過程,其代碼如下:

procedure SignalSyncEvent;
begin
  SetEvent(SyncEvent);
end;

也很簡單,就是設置一下一個全局Event:SyncEvent,關於Event的使用,本文將在後文詳述,而SyncEvent的用途將在WaitFor過程中說明。

然後根據FreeThread中保存的FreeOnTerminate設置決定是否釋放線程類,在 線程類釋放時,還有一些些操作,詳見接下來的析構函數實現。

最後調用EndThread結束線程,返回線程返回值。

至此,線程完全結束。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved