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

DllMain和多線程死鎖

發(fā)布時(shí)間:2011-08-29 共3頁(yè)

  //創(chuàng)建DLL內(nèi)的線程對(duì)象

  g_thread_handle = ::CreateThread(NULL,0,

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

  // 禁止線程庫(kù)調(diào)用,

  DisableThreadLibraryCalls((HINSTANCE)hModule);

  }

  break;

  case DLL_PROCESS_DETACH:

  // DLL正在從進(jìn)程地址空間中卸載

  {

  //   通知內(nèi)部的線程g_thread_handle 退出

  ::SetEvent(g_hEvent);

  //   等待內(nèi)部的線程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時(shí),創(chuàng)建一個(gè)   DLL內(nèi)部的線程g_thread_handle及事件對(duì)象g_hEvent,且線程g_thread_handle在事件對(duì)象g_hEvent上等待。

  (2)卸載DLL時(shí),通過(guò)SetEvent(g_hEvent)通知線程g_thread_handle退出,隨即調(diào)用 WaitForSingleObject函數(shù)等待線程g_thread_handle終止運(yùn)行。如果線程g_thread_handle終止運(yùn)行,則執(zhí)行清除工作。

  但是如果對(duì)這樣的程序進(jìn)行調(diào)試,就會(huì)發(fā)現(xiàn)程序在退出時(shí)該DllMain沒(méi)有退出,等待了很長(zhǎng)時(shí)間也沒(méi)有退出。

  查看了一下線程Call Stack窗口,注意到程序正在等待DllMain內(nèi)部的線程g_thread_handle的退出。盡管線程g_thread_handle的線程函數(shù)已經(jīng)返回了,但是整個(gè)g_thread_handle線程走到了操作系統(tǒng)的ntdll.dll中并沒(méi)有完全終止,從而導(dǎo)致整個(gè)DLL不能順利釋放。

  線程g_thread_handle為什么沒(méi)有完全退出呢?

  原來(lái),線程函數(shù)返回時(shí),系統(tǒng)并不立即將它撤消。相反,系統(tǒng)要取出這個(gè)即將被撤消的線程,讓它調(diào)用已經(jīng)映射的DLL的所有帶有 DLL_THREAD_DETACH值的、且沒(méi)有調(diào)用DisableThreadLibraryCalls函數(shù)的DllMain函數(shù)。 DLL_THREAD_DETACH通知告訴所有的DLL執(zhí)行每個(gè)線程的清除操作,例如,DLL版本的C/C++運(yùn)行期庫(kù)能夠釋放它用于管理多線程應(yīng)用程序的數(shù)據(jù)塊。DisableThreadLibraryCalls函數(shù)告訴系統(tǒng)說(shuō),特定的DLL的DllMain函數(shù)不用接收DLL_THREAD_ATTACH和DLL_THREAD_DETACH通知。

  但是,系統(tǒng)是順序調(diào)用DLL的DllMain函數(shù)的。

  當(dāng)線程函數(shù)返回時(shí),系統(tǒng)檢查進(jìn)程中是否存在沒(méi)有調(diào)用DisableThreadLibraryCalls函數(shù)的DllMain函數(shù),如果存在,系統(tǒng)就以進(jìn)程的互斥對(duì)象的句柄作為第一個(gè)參數(shù),在線程內(nèi)部調(diào)用WaitForSingleObject函數(shù)。一旦這個(gè)將要終止運(yùn)行的線程擁有該進(jìn)程互斥對(duì)象,系統(tǒng)就讓該線程用DLL_THREAD_DETACH的值依次調(diào)用每個(gè)沒(méi)有調(diào)用DisableThreadLibraryCalls函數(shù)的DLL的 DllMain函數(shù)。此后,系統(tǒng)才釋放對(duì)進(jìn)程互斥對(duì)象的所有權(quán)。

  在本例所述的應(yīng)用程序中,進(jìn)程的退出引起操作系統(tǒng)獲取進(jìn)程互斥對(duì)象使操作系統(tǒng)可以為DLL_PROCESS_DETACH通知調(diào)用 DllMain()。該DLL的DllMain()函數(shù)通知線程g_thread_handle終止運(yùn)行。無(wú)論何時(shí)當(dāng)進(jìn)程終止一個(gè)線程時(shí),操作系統(tǒng)將獲取進(jìn)程互斥對(duì)象,以便于它可以為DLL_THREAD_DETACH通知調(diào)用每個(gè)加載的、沒(méi)有調(diào)用DisableThreadLibraryCalls函數(shù)的DLL的DllMain函數(shù)。在這個(gè)特定的程序中,線程g_thread_handle當(dāng)線程函數(shù)返回后就阻塞了,因?yàn)镃MySingleton的 DllMain()所處的線程還保持著進(jìn)程互斥對(duì)象。不幸的是,DllMain所處的線程然后調(diào)用WaitForSingleObject確認(rèn) g_thread_handle線程是否完全終止。因?yàn)間_thread_handle線程被阻塞在進(jìn)程互斥對(duì)象上,這個(gè)進(jìn)程互斥對(duì)象還被DllMain 線程所持有, DllMain線程要等待g_thread_handle線程從而也被阻塞,結(jié)果就導(dǎo)致了死鎖。如下圖所示:

  注意,從圖2可以看出,如果當(dāng)前進(jìn)程中的所有 DLL都調(diào)用了DisableThreadLibraryCalls函數(shù),那么上述代碼中的DLL也能正常退出。曾經(jīng)寫(xiě)過(guò)一個(gè)程序,除了加載一個(gè)這樣有問(wèn)題的DLL沒(méi)有加載其他DLL(系統(tǒng)的DLL除外),程序能夠正常退出。

  3、結(jié)論

  很顯然的一個(gè)教訓(xùn)就是在DllMain內(nèi)部應(yīng)該避免任何Wait*調(diào)用。但是進(jìn)程互斥對(duì)象的問(wèn)題不僅僅限于Wait*函數(shù)。操作系統(tǒng)在 CreateProcess、GetModuleFileName、GetProcAddress、wglMakeCurrent、 LoadLibrary和FreeLibrary等函數(shù)中在后臺(tái)獲取進(jìn)程互斥對(duì)象,因此在DllMain中不應(yīng)該調(diào)用任何這些函數(shù)。因?yàn)镈llMain獲取進(jìn)程互斥對(duì)象,所以一次只能有一個(gè)線程執(zhí)行DllMain。

  ATL singleton的 FinalConstruct函數(shù)和FinalRelease函數(shù)分別是DllMain在響應(yīng)DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH時(shí)被調(diào)用的,所以也要同樣注意本文所述的問(wèn)題。

  編輯特別推薦:

  類成員函數(shù)的重載、覆蓋和隱藏的區(qū)別

  用C寫(xiě)最簡(jiǎn)單的Windows窗口程序:WINAPIHelloWorld

百分百考試網(wǎng) 考試寶典

立即免費(fèi)試用