精品理论电影在线_日韩视频一区二区_一本色道精品久久一区二区三区_香蕉综合视频

DllMain和多線程死鎖

發布時間:2011-08-29 共3頁

  //創建DLL內的線程對象

  g_thread_handle = ::CreateThread(NULL,0,

  InSideDll_ThreadProc,(LPVOID)0,0,   &( g_thread_id) ) ;

  // 禁止線程庫調用,

  DisableThreadLibraryCalls((HINSTANCE)hModule);

  }

  break;

  case DLL_PROCESS_DETACH:

  // DLL正在從進程地址空間中卸載

  {

  //   通知內部的線程g_thread_handle 退出

  ::SetEvent(g_hEvent);

  //   等待內部的線程g_thread_handle 退出

  ::WaitForSingleObject(g_thread_handle, INFINITE ) ;

  // 清除資源

  ::CloseHandle(g_thread_handle);

  g_thread_id = 0 ;

  g_thread_handle = NULL ;

  ::CloseHandle(g_hEvent);

  g_hEvent=NULL;

  }

  break;

  }

  return TRUE;

  }

  //----------------------end   ------------

  上述代碼的流程是這樣的:

  (1)裝載DLL時,創建一個   DLL內部的線程g_thread_handle及事件對象g_hEvent,且線程g_thread_handle在事件對象g_hEvent上等待。

  (2)卸載DLL時,通過SetEvent(g_hEvent)通知線程g_thread_handle退出,隨即調用 WaitForSingleObject函數等待線程g_thread_handle終止運行。如果線程g_thread_handle終止運行,則執行清除工作。

  但是如果對這樣的程序進行調試,就會發現程序在退出時該DllMain沒有退出,等待了很長時間也沒有退出。

  查看了一下線程Call Stack窗口,注意到程序正在等待DllMain內部的線程g_thread_handle的退出。盡管線程g_thread_handle的線程函數已經返回了,但是整個g_thread_handle線程走到了操作系統的ntdll.dll中并沒有完全終止,從而導致整個DLL不能順利釋放。

  線程g_thread_handle為什么沒有完全退出呢?

  原來,線程函數返回時,系統并不立即將它撤消。相反,系統要取出這個即將被撤消的線程,讓它調用已經映射的DLL的所有帶有 DLL_THREAD_DETACH值的、且沒有調用DisableThreadLibraryCalls函數的DllMain函數。 DLL_THREAD_DETACH通知告訴所有的DLL執行每個線程的清除操作,例如,DLL版本的C/C++運行期庫能夠釋放它用于管理多線程應用程序的數據塊。DisableThreadLibraryCalls函數告訴系統說,特定的DLL的DllMain函數不用接收DLL_THREAD_ATTACH和DLL_THREAD_DETACH通知。

  但是,系統是順序調用DLL的DllMain函數的。

  當線程函數返回時,系統檢查進程中是否存在沒有調用DisableThreadLibraryCalls函數的DllMain函數,如果存在,系統就以進程的互斥對象的句柄作為第一個參數,在線程內部調用WaitForSingleObject函數。一旦這個將要終止運行的線程擁有該進程互斥對象,系統就讓該線程用DLL_THREAD_DETACH的值依次調用每個沒有調用DisableThreadLibraryCalls函數的DLL的 DllMain函數。此后,系統才釋放對進程互斥對象的所有權。

  在本例所述的應用程序中,進程的退出引起操作系統獲取進程互斥對象使操作系統可以為DLL_PROCESS_DETACH通知調用 DllMain()。該DLL的DllMain()函數通知線程g_thread_handle終止運行。無論何時當進程終止一個線程時,操作系統將獲取進程互斥對象,以便于它可以為DLL_THREAD_DETACH通知調用每個加載的、沒有調用DisableThreadLibraryCalls函數的DLL的DllMain函數。在這個特定的程序中,線程g_thread_handle當線程函數返回后就阻塞了,因為CMySingleton的 DllMain()所處的線程還保持著進程互斥對象。不幸的是,DllMain所處的線程然后調用WaitForSingleObject確認 g_thread_handle線程是否完全終止。因為g_thread_handle線程被阻塞在進程互斥對象上,這個進程互斥對象還被DllMain 線程所持有, DllMain線程要等待g_thread_handle線程從而也被阻塞,結果就導致了死鎖。如下圖所示:

  注意,從圖2可以看出,如果當前進程中的所有 DLL都調用了DisableThreadLibraryCalls函數,那么上述代碼中的DLL也能正常退出。曾經寫過一個程序,除了加載一個這樣有問題的DLL沒有加載其他DLL(系統的DLL除外),程序能夠正常退出。

  3、結論

  很顯然的一個教訓就是在DllMain內部應該避免任何Wait*調用。但是進程互斥對象的問題不僅僅限于Wait*函數。操作系統在 CreateProcess、GetModuleFileName、GetProcAddress、wglMakeCurrent、 LoadLibrary和FreeLibrary等函數中在后臺獲取進程互斥對象,因此在DllMain中不應該調用任何這些函數。因為DllMain獲取進程互斥對象,所以一次只能有一個線程執行DllMain。

  ATL singleton的 FinalConstruct函數和FinalRelease函數分別是DllMain在響應DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH時被調用的,所以也要同樣注意本文所述的問題。

  編輯特別推薦:

  類成員函數的重載、覆蓋和隱藏的區別

  用C寫最簡單的Windows窗口程序:WINAPIHelloWorld

百分百考試網 考試寶典

立即免費試用