Delphi中的執行緒類
猛禽[Mental Studio]
http://mental.mentu.com
之五(大結局)
回到前面CheckSynchronize,請看下面的程式碼:
函數 CheckSynchronize(超時: 整數 = 0): 布林值;
變數
SyncPROc:PSyncProc;
本地同步列表:TList;
開始
如果 GetCurrentThreadID <> MainThreadID 那麼
引發 EThread.CreateResFmt(@SCheckSynchronizeError, [GetCurrentThreadID]);
如果超時 > 0 那麼
等待同步事件(超時)
別的
重置同步事件;
LocalSyncList := nil;
EnterCriticalSection(ThreadLock);
嘗試
整數(LocalSyncList):= InterlockedExchange(整數(SyncList),整數(LocalSyncList));
嘗試
結果 := (LocalSyncList <> nil) 和 (LocalSyncList.Count > 0);
如果結果那麼
開始
當 LocalSyncList.Count > 0 時
開始
SyncProc := LocalSyncList[0];
LocalSyncList.Delete(0);
LeaveCriticalSection(ThreadLock);
嘗試
嘗試
SyncProc.SyncRec.FMethod;
除了
SyncProc.SyncRec.FSynchronizeException := AcquireExceptionObject;
結尾;
最後
EnterCriticalSection(ThreadLock);
結尾;
SetEvent(SyncProc.signal);
結尾;
結尾;
最後
LocalSyncList.Free;
結尾;
最後
LeaveCriticalSection(ThreadLock);
結尾;
結尾;
首先,這個方法在主線程中被呼叫(如前面透過訊息必須傳遞到主線程),否則就發送異常。
呼叫接下來的ResetSyncEvent(它與前面SetSyncEvent對應的,不必考慮WaitForSyncEvent的情況,是因為只有在linux版本下才會呼叫帶參數的CheckSynchronize,Windows版本下都是呼叫預設參數0的CheckSynchronize)。
現在可以看出SyncList的用途了:它是用來記錄所有同步執行的同步方法的。無法處理,所以需要一個清單來記錄它們。
這裡用一個局部變數LocalSyncList來交換SyncList,這裡用的也是一個原語:InterlockedExchange。
只要LocalSyncList不為空,則透過一個循環來依序處理累積的所有同步方法呼叫。
再看看同步方法的處理:首先是從清單中移出(取出並從清單中刪除)第一個同步方法呼叫資料。
接下來才是真正的呼叫同步方法了。
如果同步方法中出現異常,將被擷取後存入同步方法資料記錄中。
重新進入臨界區後,呼叫SetEvent通知呼叫線程,同步方法執行完成了(請參閱前面Synchronize中的WaitForSingleObject呼叫)。
至此,整個同步的實作介紹完成。
最後說一下WaitFor,它的功能就是等待執行緒執行結束。
函數 TThread.WaitFor: LongWord;
變數
H: THandle 的陣列[0..1];
等待結果:Cardinal;
訊息:TM訊息;
開始
H[0] := FHandle;
如果 GetCurrentThreadID = MainThreadID 那麼
開始
等待結果:= 0;
H[1] := 同步事件;
重複
{ 如果後台線程
向前台執行緒發送 SendMessage }
如果 WaitResult = WAIT_OBJECT_0 + 2 那麼
PeekMessage(訊息, 0, 0, 0, PM_NOREMOVE);
WaitResult := MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);
CheckThreadError(WaitResult <> WAIT_FAILED);
如果 WaitResult = WAIT_OBJECT_0 + 1 那麼
檢查同步;
直到等待結果= WAIT_OBJECT_0;
結束 else WaitForSingleObject(H[0], INFINITE);
CheckThreadError(GetExitCodeThread(H[0], 結果));
結尾;
如果不是在主執行緒中執行WaitFor的話,很簡單,只需呼叫WaitForSingleObject等待此執行緒的Handle為Signaled狀態即可。
如果是在主執行緒執行WaitFor則比較麻煩。
在循環等待中作如下處理:如果有訊息發生,則透過PeekMessage取出此訊息(但不會把它從訊息循環中移除),然後呼叫MsgWaitForMultipleObjects來等待執行緒Handle或SyncEvent出現Signaled狀態,同時監聽訊息( QS_SENDMESSAGE參數,請參閱MSDN中關於此API的說明)。
在主執行緒中呼叫WaitFor必須用MsgWaitForMultipleObjects,而不能用WaitForSingleObject等待執行緒結束呢?的,如果使用WaitForSingleObject等待的話,則主執行緒在這裡被掛起,同步方法無法執行,導致執行緒也被掛起,從而發生死鎖。
而改用WaitForMultipleObjects則沒有這個問題。主執行緒來的,所以還要防止訊息被阻塞。
至此,對於線程類別TThread的分析可以告一個段落了,對前面的分析作一個總結:
1、 執行緒類別的執行緒必須以正常的方式結束,即執行執行結束,所以在其中的程式碼中必須在適當的位置加入足夠多的對終止標誌的判斷,並及時退出。則不能使用線程類,而要改用API或RTL函數。
2、 對可視VCL的存取要放在Synchronize中,透過訊息傳遞到主執行緒中,由主執行緒處理。
3. 執行緒共享資料的存取應該用臨界區來保護(當然用Synchronize也行)。
4. 線程通訊可以採用Event進行(當然也可以用Suspend/Resume)。
5.在多執行緒應用中使用多種執行緒同步方式時,一定要小心防止死鎖出現。
6.等待執行緒結束使用WaitFor方法。
12月1-3日
(終於結束了)