Delphi中的執行緒類(2)

2021-04-24 09:40:42 字數 3933 閱讀 1349

首先就是建構函式:

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);

現在來看tthread的核心:執行緒函式threadproc。有意思的是這個執行緒類的核心卻不是執行緒的成員,而是乙個全域性函式(因為beginthread過程的引數約定只能用全域性函式)。下面是它的**:

function threadproc(thread: tthread): integer;

varfreethread: boolean;

begin

tryif not thread.terminated then

trythread.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;

首先判斷執行緒類的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過程中說明。

最後呼叫endthread結束執行緒,返回執行緒返回值。

至此,執行緒完全結束。

(待續)

Delphi中的執行緒類 之(3)

delphi中的執行緒類 猛禽 mental studio 之三說完建構函式,再來看析構函式 destructor tthread.destroy begin if fthreadid 0 and not ffinished then begin terminate if fcreatesuspen...

Delphi中多執行緒中Synchronize的運用

delphi中多執行緒用synchronize實現vcl資料同步顯示,delphi中多執行緒用synchronize實現vcl資料同步顯示 概述 vcl實現同步的另一種方法就是呼叫執行緒類的synchronize的過程,此過程需要乙個無引數的procedure,故在此procedure中無法傳遞引數...

Delphi中TList類應用

在delphi中指標最常見的就是和類tlist結合起來使用。下面是乙個很簡單的例子,希望對這個例子的分析能讓大家對使用tlist類有乙個簡單的認識。的功能是使用指標和tlist來生成乙個牌串,並將牌串儲存在t cardinfo中。procedure tform1.button1click sende...