首先就是建構函式:
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...