windows核心編程之使用線程APC回調安全退出多個等待線程

來源:互聯網
上載者:User

標籤:

前言

程式開發中經常遇到需要這些情況:輔助線程正在等待核心對象的觸發,主線程需要強制終止輔助線程。我們常常做的就是使用:TerminateThread來強制終止線程。這樣做當然是不太好的,強制終止線程後系統不會銷毀此線程的堆棧,長久下去記憶體泄露問題就會很嚴重了。線程最安全的退出方式當然還是讓它自己返回了。本文主要介紹windows核心編程中介紹的一種安全退出線程方式:使用可等待API等待核心對象觸發,添加線程APC回調。

API介紹

首先得簡單介紹下一個重要的windows API

DWORD WINAPI QueueUserAPC(  __in          PAPCFUNC pfnAPC,  __in          HANDLE hThread,  __in          ULONG_PTR dwData);

函數允許我們手動將一個APC回調添加到指定線程隊列中,回呼函數:VOID WINAPI ApcCallback1(ULONG_PTR dwParam)
這個線程可以是系統中任何線程,如果標識的線程在另一個進程中,那麼回呼函數的地址也必須在另一個線程的地址空間中。
QueueUserAPC也可以用來強制讓線程退出等待狀態,當線程正在等待核心對象觸發,可以使用它強制喚醒正在等待的線程,並讓它將自己殺死。

另外就是Windows提供了6個API,可將線程的狀態設定為可提醒狀態。

SleepEx(
__in DWORD dwMilliseconds,
__in BOOL bAlertable
);
WaitForSingleObjectEx(
__in HANDLE hHandle,
__in DWORD dwMilliseconds,
__in BOOL bAlertable
);
WaitForMultipleObjectsEx(
__in DWORD nCount,
__in_ecount(nCount) CONST HANDLE *lpHandles,
__in BOOL bWaitAll,
__in DWORD dwMilliseconds,
__in BOOL bAlertable
);
SignalObjectAndWait(
__in HANDLE hObjectToSignal,
__in HANDLE hObjectToWaitOn,
__in DWORD dwMilliseconds,
__in BOOL bAlertable
);
GetQueuedCompletionStatusEx
MsgWaitForMultipleObjectsEx 使用MWMO_ALERTABLE標識線程進入可等待狀態
這五個函數的老版本API,即不帶Ex的在內部實現都是調用對應的Ex函數,傳入bAlertable=FALSE。

在可提醒I/O中將對這些函數的調用以及完成回調、APC回調進行詳細的介紹,這裡暫時省略了。

執行個體代碼

//給每一個線程標記上一個索引值volatile LONGg_nIndex = 0;UINT __stdcall Thread1(void* lpParam){//使用原子鎖來操作實現使用者模式下的線程同步,更加快捷LONG lPrevIndex = InterlockedExchangeAdd(&g_nIndex, 1);//我們需要記錄原始值LONG lCurIndex= lPrevIndex + 1;cout<<"線程"<<lCurIndex<<"開始執行"<<endl;while( true ){//cout<<"Thread1:"<<nIndex<<endl;//休眠一秒鐘,線程進入可提醒狀態if ( WAIT_IO_COMPLETION == SleepEx(1000, TRUE) ){//此時,我們添加一項到APC隊列中了,線程可以退出了break;}//nIndex++;}cout<<"線程"<<lCurIndex<<"正常退出"<<endl;return 0;}//APC回呼函數VOID WINAPI ApcCallback(ULONG_PTR dwParam){//由於只是為了讓線程立即正常退出,而不必殺死線程,我們在這裡可以不做任何處理cout<<"ApcCallback:線程的APC回呼函數執行"<<endl;}bool ApcTest(){const int nThreadCount = 10;HANDLE hThread[nThreadCount];int i = 0; for ( int i=0; i<nThreadCount; ++i )hThread[i] = (HANDLE)_beginthreadex(NULL, 0, Thread1, NULL, 0, NULL);DWORD dwError, dwRet;while( true ){dwRet = WaitForMultipleObjects(nThreadCount, hThread, TRUE, 10*1000);if ( dwRet == WAIT_TIMEOUT ){//等待逾時,想辦法強制終止線程//由於輔助線程休眠時進入可等待狀態,手動添加回調到APC隊列將導致毀掉執行完後線程清空隊列,線程退出可提醒狀態//此時,友好地退出線程。for ( i=0; i<nThreadCount; ++i ){dwRet = QueueUserAPC(ApcCallback, hThread[i], 2015);if ( dwRet == 0 ){//添加APC回調到線程APC隊列失敗dwError = GetLastError();break;}}continue;}if ( WAIT_OBJECT_0 == dwRet ){cout<<"輔助線程均已退出,迴圈退出…………"<<endl;break;}}for ( i=0; i<nThreadCount; ++i )CloseHandle(hThread[i]);return true;}

程式運行


總結

主要就是線上程等待時,使用那些擴充的API(帶Ex),使線程進入可等待狀態。這時,系統會先檢查線程的APC隊列。由於我們向線程的APC隊列中人為添加了一個回呼函數,隊列不為空白則調用APC函數,清空隊列。然後函數執行完畢返回WAIT_IO_COMPLETION,這時我們就知道是自己添加了一個APC回調,引導線程自己退出。從而實現多個等待安全執行緒地退出等待狀態。

windows核心編程之使用線程APC回調安全退出多個等待線程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.