国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Delphi > 正文

Delphi中的線程類--之(5,大結局)

2019-11-18 18:27:57
字體:
來源:轉載
供稿:網友
 

Delphi中的線程類

 

猛禽[Mental Studio]

http://mental.mentsu.com

 

之五(大結局)

回到前面CheckSynchronize,見下面的代碼:

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;

首先,這個方法必須在主線程中被調用(如前面通過消息傳遞到主線程),否則就拋出異常。

接下來調用ResetSyncEvent(它與前面SetSyncEvent對應的,之所以不考慮WaitForSyncEvent的情況,是因為只有在linux版下才會調用帶參數的CheckSynchronizeWindows版下都是調用默認參數0CheckSynchronize)。

現在可以看出SyncList的用途了:它是用于記錄所有未被執行的同步方法的。因為主線程只有一個,而子線程可能有很多個,當多個子線程同時調用同步方法時,主線程可能一時無法處理,所以需要一個列表來記錄它們。

在這里用一個局部變量LocalSyncList來交換SyncList,這里用的也是一個原語:InterlockedExchange。同樣,這里也是用臨界區將對SyncList的訪問保護起來。

只要LocalSyncList不為空,則通過一個循環來依次處理累積的所有同步方法調用。最后把處理完的LocalSyncList釋放掉,退出臨界區。

再來看對同步方法的處理:首先是從列表中移出(取出并從列表中刪除)第一個同步方法調用數據。然后退出臨界區(原因當然也是為了防止死鎖)。

接著就是真正的調用同步方法了。

如果同步方法中出現異常,將被捕獲后存入同步方法數據記錄中。

重新進入臨界區后,調用SetEvent通知調用線程,同步方法執行完成了(詳見前面Synchronize中的WaitForSingleObject調用)。

至此,整個Synchronize的實現介紹完成。

 

最后來說一下WaitFor,它的功能就是等待線程執行結束。其代碼如下:

function TThread.WaitFor: LongWord;

var

  H: array[0..1] of THandle;

  WaitResult: Cardinal;

  Msg: TMsg;

begin

  H[0] := FHandle;

  if GetCurrentThreadID = MainThreadID then

  begin

    WaitResult := 0;

    H[1] := SyncEvent;

    repeat

      { This prevents a potential deadlock if the background thread

        does a SendMessage to the foreground thread }

      if WaitResult = WAIT_OBJECT_0 + 2 then

        PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);

      WaitResult := MsgWaitForMultipleObjects(2, H, False, 1000, QS_SENDMESSAGE);

      CheckThreadError(WaitResult <> WAIT_FAILED);

      if WaitResult = WAIT_OBJECT_0 + 1 then

        CheckSynchronize;

    until WaitResult = WAIT_OBJECT_0;

  end else WaitForSingleObject(H[0], INFINITE);

  CheckThreadError(GetExitCodeThread(H[0], Result));

end;

如果不是在主線程中執行WaitFor的話,很簡單,只要調用WaitForSingleObject等待此線程的HandleSignaled狀態即可。

如果是在主線程中執行WaitFor則比較麻煩。首先要在Handle數組中增加一個SyncEvent,然后循環等待,直到線程結束(即MsgWaitForMultipleObjects返回WAIT_OBJECT_0,詳見MSDN中關于此API的說明)。

在循環等待中作如下處理:如果有消息發生,則通過PeekMessage取出此消息(但并不把它從消息循環中移除),然后調用MsgWaitForMultipleObjects來等待線程HandleSyncEvent出現Signaled狀態,同時監聽消息(QS_SENDMESSAGE參數,詳見MSDN中關于此API的說明)。可以把此API當作一個可以同時等待多個HandleWaitForSingleObject。如果是SyncEventSetEvent(返回WAIT_OBJECT_0 + 1),則調用CheckSynchronize處理同步方法。

為什么在主線程中調用WaitFor必須用MsgWaitForMultipleObjects,而不能用WaitForSingleObject等待線程結束呢?因為防止死鎖。由于在線程函數Execute中可能調用Synchronize處理同步方法,而同步方法是在主線程中執行的,如果用WaitForSingleObject等待的話,則主線程在這里被掛起,同步方法無法執行,導致線程也被掛起,于是發生死鎖。

而改用WaitForMultipleObjects則沒有這個問題。首先,它的第三個參數為False,表示只要線程HandleSyncEvent中只要有一個Signaled即可使主線程被喚醒,至于加上QS_SENDMESSAGE是因為Synchronize是通過消息傳到主線程來的,所以還要防止消息被阻塞。這樣,當線程中調用Synchronize時,主線程就會被喚醒并處理同步調用,在調用完成后繼續進入掛起等待狀態,直到線程結束。

 

至此,對線程類TThread的分析可以告一個段落了,對前面的分析作一個總結:

1、  線程類的線程必須按正常的方式結束,即Execute執行結束,所以在其中的代碼中必須在適當的地方加入足夠多的對Terminated標志的判斷,并及時退出。如果必須要“立即”退出,則不能使用線程類,而要改用APIRTL函數。

2、  對可視VCL的訪問要放在Synchronize中,通過消息傳遞到主線程中,由主線程處理。

3、  線程共享數據的訪問應該用臨界區進行保護(當然用Synchronize也行)。

4、  線程通信可以采用Event進行(當然也可以用Suspend/Resume)。

5、  當在多線程應用中使用多種線程同步方式時,一定要小心防止出現死鎖。

6、  等待線程結束要用WaitFor方法。

 

Dec.01-03

(終于續完了)


上一篇:用DELPHI實現NT環境下的絕對磁盤讀寫

下一篇:Delphi中的線程類--之(4)

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
學習交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網友關注

主站蜘蛛池模板: 宜宾市| 赤峰市| 宿迁市| 吉木萨尔县| 临朐县| 大同市| 连州市| 东山县| 钟山县| 镇江市| 凌海市| 库尔勒市| 合阳县| 泰州市| 神农架林区| 调兵山市| 吉安市| 黄石市| 宁化县| 韶山市| 黎川县| 五寨县| 丁青县| 建水县| 新郑市| 英超| 株洲县| 曲麻莱县| 扎赉特旗| 保山市| 阿勒泰市| 枣庄市| 海阳市| 沽源县| 丰台区| 札达县| 华池县| 徐汇区| 宜黄县| 福建省| 千阳县|