《Windows via C/C++》學習筆記 —— “線程同步”之“檢測死結”

來源:互聯網
上載者:User

  本來這篇內容在書中是在“其他線程同步函數”這一節中的。這節中介紹了另外的幾個等待函數,比如WaitForInputIdle、MsgWaitForMultipleObjects、WaitForDebugEvent,感覺用途不大,只有SignalObjectAndWait這個函數用途比較大,該函數筆者已經在前面的“等待函數”這一篇中寫過了。

  本篇主要討論這本書新增加的內容,就是Windows Vista中提供的調查死結的方法。

 

  在Windows Vista中,提供了用來“檢測線程死結”的一組API,Jeffrey Richter先生在書中將其稱為“Wait Chain Traversal”(WCT)API。查看了網上的翻譯,叫做“等待鏈遍曆”API函數,我也不知道這麼翻譯是否正確或權威,就姑且這麼叫著吧,下面簡稱為WCT。

  WCT函數允許你列出“鎖”,並檢測死結,無論是單個進程的,還是跨進程的。Windows對以下線程同步機制的鎖保持著跟蹤:

可能的鎖

描述

關鍵程式碼片段

Windows對關鍵程式碼片段屬於哪個線程保持著跟蹤

互斥核心對象

Windows對互斥核心對象屬於哪個線程保持著跟蹤,即使互斥核心對象被丟棄

進程和線程

Windows對一個線程正在等待哪個進程或線程保持著跟蹤,另外跟蹤進程和線程是否終止

SendMessage

Windows保持跟蹤正在等待SendMessage函數返回的線程

COM初始化和調用

調用CoCreateInstance函數或COM對象的方法的線程被跟蹤

進階本地程序呼叫(ALPC)

在Windows Vista中,ALPC代替了LPC,新的沒有文檔的核心進程內部的通訊(IPC)機制

 

  需要注意的是,讀寫鎖(SRWLock)同步機制沒有被WPC所跟蹤,另外某些核心對象,比如事件核心對象、訊號量、等待定時器沒有被跟蹤。

 

  本書舉了一個例子來示範了WPC函數的使用。本人總結了下,主要通過以下幾個步驟來實現死結的檢測:

1、開啟等待鏈:使用OpenThreadWaitChainSession函數

2、如果需要檢測COM的話:使用RegisterWaitChainCOMCallback函數

3、取得等待鏈資訊:使用GetThreadWaitChain函數

4、關閉等待鏈:使用CloseThreadWaitChainSession函數

 

  首先,介紹一下OpenThreadWaitChainSession函數:

HWCT OpenThreadWaitChainSession(
   DWORD dwFlags,       //0表示同步,WCT_ASYNC_OPEN_FLAG表示非同步
   PWAITCHAINCALLBACK callback);   //非同步話要設定該回呼函數指標

 

  該函數成功,返回一個HWCT類型的控制代碼,該控制代碼要傳遞給CloseThreadWaitChainSession函數,在最後釋放。如果失敗,該函數返回NULL。

  一般地,您總是希望從中調用RegisterWaitChainCOMCallback函數以註冊 CoGetCallState和CoGetActivationState這兩個COM函數,以便WCT能夠報告 COM所有權資訊,由於並未記錄這些函數,您需要從ole32.dll通過GetProcAddress 獲得這些函數。本人認為,在您對WCT API進行初始化以尋找COM死結時,WCT API 應自動處理這種細小問題。

 

VOID WINAPI RegisterWaitChainCOMCallback(
   __in  PCOGETCALLSTATE CallStateCallback,   //CoGetCallState函數地址
   __in  PCOGETACTIVATIONSTATE 
             ActivationStateCallback    //CoGetActivationState函數地址
);

 

  你可以使用如下代碼這樣使用RegisterWaitChainCOMCallback函數:

 

PCOGETCALLSTATE CallStateCallback;      //函數指標
PCOGETACTIVATIONSTATE ActivationStateCallback;     //函數指標

HMODULE hOLE32DLL = LoadLibrary(TEXT("OLE32.DLL"));//載入OLE32.DLL

//從ole32.dll中取得函數地址
CallStateCallback = (PCOGETCALLSTATE)
                     GetProcAddress(_hOLE32DLL, "CoGetCallState");
ActivationStateCallback = (PCOGETACTIVATIONSTATE)
             GetProcAddress(_hOLE32DLL, "CoGetActivationState"); 

//在等待鏈中註冊COM函數
RegisterWaitChainCOMCallback(CallStateCallback,ActivationStateCallback);

 

  下面,第3步,使用GetThreadWaitChain函數取得等待鏈資訊:

BOOL WINAPI GetThreadWaitChain(
   HWCT hWctSession,          //等待鏈控制代碼
   DWORD_PTR pContext,     //非同步方法呼叫中回呼函數的參數
   DWORD dwFlags,              //查詢類型(位或組合)
   DWORD TID,                    //要查詢的線程的線程ID
   PDWORD pNodeCount,      //傳回值,指明等待鏈中的結點個數
   PWAITCHAIN_NODE_INFO pNodeInfoArray,     //傳回值,等待鏈結點資訊
   LPBOOL pbIsCycle             //傳回值,TURE表示死結
);

 

  pNodeCount參數如果返回的DWORD資料值為0,表明一個問題產生了,比如拒絕訪問。這個時候可以調用GetLastError取得錯誤資訊。

  這個函數中,pNodeInfoArray函數是一個WAITCHAIN_NODE_INFO結構的指標,該結構如下:

 

typedef struct _WAITCHAIN_NODE_INFO
{
    WCT_OBJECT_TYPE ObjectType;          //結點物件類型
    WCT_OBJECT_STATUS ObjectStatus;   //結點對象狀態

    union {
        struct {
            WCHAR ObjectName[WCT_OBJNAME_LENGTH];
            LARGE_INTEGER Timeout;    // Not implemented in v1
            BOOL Alertable;           // Not implemented in v1
        } LockObject;

       struct {
           DWORD ProcessId;
           DWORD ThreadId;
           DWORD WaitTime;
           DWORD ContextSwitches;
       } ThreadObject;
    };
} WAITCHAIN_NODE_INFO, *PWAITCHAIN_NODE_INFO;

 

  該結構中的ObjectTyped成員,指明了當前等待鏈中的結點類型,ObjectStatus指明了當前該結點對象的狀態,兩者具體取值就看MSDN或本書吧。如果這個結點類型是一個“阻塞”狀態的線程,即ObjectTyped為WctThreadType的話,內部的ThreadObject成員即有效。

 

  最後,不要忘記調用CloseThreadWaitChainSession關閉WCT控制代碼。當然,如果用LoadLibrary函數載入了OLE23.dll檔案的話,也要記得FreeLibrary函數來釋放。

 

  可以使用如下代碼處理等待鏈中的結點:

 

HWCT hWCTSession = OpenThreadWaitChainSession(0, NULL);
if (hWCTSession == NULL)
     return 0;

//兩個COM的函數指標
PCOGETCALLSTATE CallStateCallback;
PCOGETACTIVATIONSTATE ActivationStateCallback;

HMODULE hOLE32DLL = LoadLibrary(TEXT("OLE32.DLL"));   //載入OLE32.DLL
if (hOLE32DLL == NULL)
     return 0;

//從ole32.dll中取得兩個函數的地址
CallStateCallback = (PCOGETCALLSTATE)
                     GetProcAddress(_hOLE32DLL, "CoGetCallState");
ActivationStateCallback = (PCOGETACTIVATIONSTATE)
                     GetProcAddress(_hOLE32DLL, "CoGetActivationState");

//註冊COM函數,使得WCT可以報告COM相關事件
RegisterWaitChainCOMCallback(CallStateCallback,ActivationStateCallback);

//在你需要處理的線程中:
DWORD dwNodesInChain = 0;     //鏈中的結點個數
BOOL bDeadlock = FALSE;       //線程是否存在死結
WAITCHAIN_NODE_INFO chain[WCT_MAX_NODE_COUNT];//WCT節點結構數組

//取得等待鏈資訊,並判斷是否正確,TID為線程ID
if (!GetThreadWaitChain(hWCTSession, NULL, WCTP_GETINFO_ALL_FLAGS,
         TID, &dwNodesInChain, chain, &bDeadlock))
{
     //函數調用失敗,在此處理
      //
     return 0;
}

// 開始處理等待鏈
dwNodesInChain = min(dwNodesInChain, WCT_MAX_NODE_COUNT);

if (dwNodesInChain == 0)     //結點個數為0,可以調用GetLastError處理
     return 0;

//可以在此處理本線程有關的等待鏈資訊
if (bDeadlock)
     //表明線程ID為TID的線程存在死結

//處理每個鏈中的節點
for (DWORD current = 0; current < dwNodesInChain; ++current)
{
     //在此處理練中每個結點資訊
      //
}

FreeLibrary(hOLE32DLL);    //釋放COM模組
CloseThreadWaitChainSession(hWCTSession);    //關閉WCT

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.