秒殺多線程第十五篇 關鍵段,事件,互斥量,信號量的“遺棄”問題
在《 秒殺多線程第九篇 經典線程同步總結 關鍵段 事件 互斥量 信號量 》中對 經典多線程同步互斥問題 進行了回顧和總結,這篇文章對Windows系統下常用的線程同步互斥機制—— 關鍵段 、 事件 、 互斥量 、 信號量 進行了總結。有網友問到互斥量能處理“遺棄”問題,事件和信號量是否也能處理“遺棄”問題。因此本文將對事件和信號量作個試驗,看看事件和信號量能否處理“遺棄”問題。
?
一.什么是“遺棄”問題
在《 秒殺多線程第七篇 經典線程同步 互斥量Mutex 》講到了互斥量能處理“遺棄”問題,下面引用原文:
互斥量常用于多進程之間的線程互斥,所以它比關鍵段還多一個很有用的特性 ——“遺棄 ”情況的處理。比如有一個占用互斥量的線程在調用ReleaseMutex() 觸發互斥量前就意外終止了(相當于該互斥量被 “遺棄 ”了),那么所有等待這個互斥量的線程是否會由于該互斥量無法被觸發而陷入一個無窮的等待過程中了?這顯然不合理。因為占用某個互斥量的線程既然終止了那足以證明它不再使用被該互斥量保護的資源,所以這些資源完全并且應當被其它線程來使用。因此在這種“遺棄 ”情況下,系統自動把該互斥量內部的線程ID 設置為 0 ,并將它的遞歸計數器復置為 0 ,表示這個互斥量被觸發了。然后系統將 “ 公平地 ” 選定一個等待線程來完成調度(被選中的線程的 WaitForSingleObject()會返回WAIT_ABANDONED_0) 。
可見“遺棄”問題就是——占有某種資源的進程意外終止后,其它等待該資源的進程能否感知。
?
二.關鍵段的“遺棄”問題
關鍵段在這個問題上很簡單——由于關鍵段不能跨進程使用,所以關鍵段不需要處理“遺棄”問題。
?
三.事件,互斥量,信號量的“遺棄”問題
事件,互斥量,信號量都是內核對象,可以跨進程使用。一個進程在創建一個命名的事件后 , 其它進程可以調用 OpenEvent() 并傳入事件的名稱來獲得這個事件的句柄。因此事件,互斥量和信號量都會遇到“遺棄”問題。我們已經知道互斥量能夠處理“遺棄”問題,接下來就來看看事件和信號量是否能夠處理“遺棄”問題。類似于《 秒殺多線程第七篇 經典線程同步互斥量Mutex 》 對互斥量所做的試驗,下面也對事件和信號量作同樣的試驗:
1.? 創建二個進程。
2.?進程一創建一個初始為未觸發的事件,然后等待按鍵,按下y 則觸發事件后結束進程,否則直接退出表示進程一已意外終止。
3.?進程二先獲得事件的句柄,然后調用WaitForSingleObject()等待這個事件10 秒,在這10 秒內如果事件已經觸發則輸出“已收到信號”,否則輸出“未在規定的時間內收到信號”。如果在等待的過程中進程一意外終止,則輸出“ 擁有事件的進程意外終止” 。信號量的試驗方法類似。
為了加強對比效果,將互斥量的試驗結果先展示出來(代碼請參見《 秒殺多線程第七篇經典線程同步 互斥量Mutex 》)
可以看出在第一個進程在沒有觸發互斥量就直接退出的情況下,等待這個互斥量的第二個進程是能夠感知進程一所發生的意外終止的。
接下來就先完成事件的“遺棄”問題試驗代碼。
進程一:
[cpp] ?view plaincopy
#include?<stdio.h> ?? #include?<conio.h> ?? #include?<windows.h> ?? const ? TCHAR ?STR_EVENT_NAME[]?=?TEXT( "Event_MoreWindows" );?? int ?main()?? {?? ????printf("?????經典線程同步?事件的遺棄處理??進程一\n" );???? ????printf("?--?by?MoreWindows(?http://blog.csdn.net/MoreWindows?)?--\n\n" );???? ????HANDLE ?hEvent?=?CreateEvent(NULL,?FALSE,?FALSE,?STR_EVENT_NAME); ?? ????printf("事件已經創建,現在按y觸發事件,按其它鍵終止進程\n" );?? ????char ?ch;?? ????scanf("%c" ,?&ch);?? ????if ?(ch?!=? 'y' )?? ????????exit(0);??? ????SetEvent(hEvent);?? ????printf("事件已經觸發\n" );?? ????CloseHandle(hEvent);?? ????return ?0;?? }??
進程二:
[cpp] ?view plaincopy
#include?<stdio.h> ?? #include?<windows.h> ?? const ? TCHAR ?STR_EVENT_NAME[]?=?TEXT( "Event_MoreWindows" );?? int ?main()?? {?? ????printf("?????經典線程同步?事件的遺棄處理??進程二\n" );???? ????printf("?--?by?MoreWindows(?http://blog.csdn.net/MoreWindows?)?--\n\n" );???? ?? ????HANDLE ?hEvent?=?OpenEvent(EVENT_ALL_ACCESS,?TRUE,?STR_EVENT_NAME);? ?? ????if ?(hEvent?==?NULL)?? ????{?? ????????printf("打開事件失敗\n" );?? ????????return ?0;?? ????}?? ????printf("?等待中....\n" );?? ????DWORD ?dwResult?=?WaitForSingleObject(hEvent,?10?*?1000);? ?? ????switch ?(dwResult)?? ????{?? ????case ?WAIT_ABANDONED:?? ????????printf("擁有事件的進程意外終止\n" );?? ????????break ;?? ?? ????case ?WAIT_OBJECT_0:?? ????????printf("已經收到信號\n" );?? ????????break ;?? ?? ????case ?WAIT_TIMEOUT:?? ????????printf("未在規定的時間內收到信號\n" );?? ????????break ;?? ????}?? ????CloseHandle(hEvent);?? ????return ?0;?? }??
事件Event 試驗結果1- 進程一觸發事件后正常結束:
事件Event 試驗結果2- 進程一意外終止:
可以看出進程二沒能感知進程一意外終止,說明事件不能處理“遺棄”問題。
?
下面再來試下信號量。
信號量的“遺棄”問題試驗代碼:
進程一:
[cpp] ?view plaincopy
#include?<stdio.h> ?? #include?<conio.h> ?? #include?<windows.h> ?? const ? TCHAR ?STR_SEMAPHORE_NAME[]?=?TEXT( "Semaphore_MoreWindows" );?? int ?main()?? {?? ????printf("?????經典線程同步?信號量的遺棄處理??進程一\n" );???? ????printf("?--?by?MoreWindows(?http://blog.csdn.net/MoreWindows?)?--\n\n" );???? ?? ????HANDLE ?hSemaphore?=?CreateSemaphore(NULL,?0,?1,?STR_SEMAPHORE_NAME); ?? ????printf("信號量已經創建,現在按y觸發信號量,按其它鍵終止進程\n" );?? ????char ?ch;?? ????scanf("%c" ,?&ch);?? ????if ?(ch?!=? 'y' )?? ????????exit(0);??? ????ReleaseSemaphore(hSemaphore,?1,?NULL);?? ????printf("信號量已經觸發\n" );?? ????CloseHandle(hSemaphore);?? ????return ?0;?? }??
進程二:
[cpp] ?view plaincopy
#include?<stdio.h> ?? #include?<windows.h> ?? const ? TCHAR ?STR_SEMAPHORE_NAME[]?=?TEXT( "Semaphore_MoreWindows" );?? int ?main()?? {?? ????printf("?????經典線程同步?信號量的遺棄處理??進程二\n" );???? ????printf("?--?by?MoreWindows(?http://blog.csdn.net/MoreWindows?)?--\n\n" );???? ?? ????HANDLE ?hSemaphore?=?OpenSemaphore?(SEMAPHORE_ALL_ACCESS,?TRUE,?STR_SEMAPHORE_NAME);? ?? ????if ?(hSemaphore?==?NULL)?? ????{?? ????????printf("打開信號量失敗\n" );?? ????????return ?0;?? ????}?? ????printf("?等待中....\n" );?? ????DWORD ?dwResult?=?WaitForSingleObject(hSemaphore,?10?*?1000);? ?? ????switch ?(dwResult)?? ????{?? ????case ?WAIT_ABANDONED:?? ????????printf("擁有信號量的進程意外終止\n" );?? ????????break ;?? ?? ????case ?WAIT_OBJECT_0:?? ????????printf("已經收到信號\n" );?? ????????break ;?? ?? ????case ?WAIT_TIMEOUT:?? ????????printf("未在規定的時間內收到信號\n" );?? ????????break ;?? ????}?? ????CloseHandle(hSemaphore);?? ????return ?0;?? }??
信號量Semaphore 試驗結果1- 進程一觸發信號量后正常結束
信號量Semaphore 試驗結果2- 進程一意外終止
可以看出進程二沒能感知進程一意外終止,說明信號量與事件一樣都不能處理“遺棄”問題。
?
四.“遺棄”問題總結
由本文所做的試驗可知,互斥量能夠處理“遺棄”情況,事件與信號量都無法解決這一情況。
再思考下互斥量能處理“遺棄”問題的原因,其實正是因為它有“線程所有權”概念。在系統中一旦有線程結束后,系統會判斷是否有互斥量被這個線程占有,如果有,系統會將這互斥量對象內部的線程 ID 號將設置為 NULL ,遞歸計數設置為 0 ,這表示該互斥量已經不為任何線程占用,處于觸發狀態。其它等待這個互斥量的線程就能順利執行下去了。至于線程如何獲取互斥量的“線程所有權”, MSDN上介紹為—— A thread obtainsownership of a mutex either by creating it with the?bInitialOwnerparameter set to?TRUE ?or by specifying its handle in a call toone of the? wait functions .
文章到這就結束了,有問題歡迎留言或發送郵件: morewindows@126.com
?
?
?
轉載請標明出處,原文地址:http://blog.csdn.net/morewindows/article/details/7823572
如果覺得本文對您有幫助,請點擊‘ 頂’ 支持一下,您的支持是我寫作最大的動力,謝謝。
?
?
?
最后號外一下:《白話經典算法系列之七大排序》已經整理出電子書形式了,歡迎大家下載,下載地址:http://download.csdn.net/detail/morewindows/4443208 。
總結
以上是生活随笔 為你收集整理的秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。