WIN32多線程程式設計學習筆記(第四章 上) 到目前為止,我已經知道如何建立線程,銷毀線程及如何判斷一個線程是否結束了;但運用線程,管理線程,如何使線程按自己意願正確運行才是多線程程式設計的重點及痛點。 大家知道,線程具有並發性,在同一時間段會存在多個線程,當這些線程同時存取同一資料時,就會有問題。就像在超市儲物品一樣,來的時候物品箱是空,轉身拿物品準備儲的時候,發現物品箱已被佔用了。這時,物品箱就是我所說的同一資料,人指的就是線程了。 那麼,如何讓線程彼此互相合作,互不干擾呢?------協調線程的工作是由同步機制來完成的。正如書上所說:“有放多種同步機制可以運用。使用哪一種則完全視欲解決的問題而定。這些同步機制還能用各種方式組合在一起運用,以產生更精密的機制”。 首先,讓我們看看最容易使用的一個同步機制;它包含同步機制的設計思想,拿握了它也就可以不費什麼勁拿握其它的同步機制了。
1
:Critical Sections(
關鍵地區、臨界地區) 程式片段:
CRITICAL_SECTION gBoxKey ;
DWORD WINAPI ThreadFun(LPVOID n)
{
//
進入關鍵地區
(情景:關上物品箱,撥下鑰匙)
EnterCreiticalSection (&gBoxKey ) ;
(1
)
//
處理一些不可分割的操作。。。。。
//
(情景:轉身拿物品,儲物品,去購物。。。。)
//
離開關鍵地區
(情景:開啟物品箱,拿出儲存的物品,插上鑰匙)
LeaveCreiticalSection (&gBoxKey) ;
(2
)
}
void main()
{
//
初始化全域鎖 (
情景:產生物品箱的鑰匙)
InitializeCriticalSection( &gBoxKey ) ;
//
產生兩個線程 (
情景:準備兩個人搶一個物品箱)
HANDLE hMan1 = CreateThread(NULL,0,ThreadFun,…….);
HANDLE hMan2 = CreateThread(NULL,0,ThreadFun,…….);
CloseHandle(hMan1);
CloseHandle(hMan2);
//
刪除全域鎖 (
情景:刪除物品箱的鑰匙)
DeleteCriticalSection( &gBoxKey ) ;
}
按照上面的程式,我在筆記開頭所描述的“超市儲物品”的情況是不會發生的,不會再有物品箱被人搶走的可能。由於線程的並發性,從我看到一個空箱子到我將物品的放入物品箱中的這段時間內的操作應該是不可分割的;否則,我看到的物品箱就隨時會被人搶走。那麼如何保證操作是連續、不可分割呢?簡單:看到空箱子,關上箱子,撥下鑰匙(1),這時,別人就沒辦法使用這個箱子了。當我不再需要箱子的時候,開啟箱子,插入鑰匙(2),就可以讓別人使用了。上述就是多執行緒共用資源的一個基本思路。
2
:Mutexes(
互斥器) 程式片段:
HANDLE hBoxKey ;
DWORD WINAPI ThreadFun(LPVOID n)
{
//
進入關鍵地區
(情景:關上物品箱,撥下鑰匙)
WaitForSingleObject ( hMutex,INFINITE ) ;
(1
)
//
處理一些不可分割的操作。。。。。
//
(情景:轉身拿物品,儲物品,去購物。。。。)
/
離開關鍵地區
(情景:開啟物品箱,拿出儲存的物品,插上鑰匙)
ReleaseMutex ( hMutex ) ;
(2
)
}
void main()
{
//
初始化全域鎖 (
情景:產生物品箱的鑰匙)
hBoxKey = CreateMutex( NULL,FALSE,NULL );
//
產生兩個線程 (
情景:準備兩個人搶一個物品箱)
HANDLE hMan1 = CreateThread(NULL,0,ThreadFun,…….);
HANDLE hMan2 = CreateThread(NULL,0,ThreadFun,…….);
CloseHandle(hMan1);
CloseHandle(hMan2);
//
刪除全域鎖 (
情景:刪除物品箱的鑰匙)
CloseHandle( hBoxKey ) ;
}看到沒?無論從共用資源的思路了,還是從程式碼的編製上,使用
Mutexes與使用
Critical Sections幾乎都沒有什麼區別;但作為
Mutexes機制的提出肯定是有其原因的;我們來看這樣的一個情形,當我拿走鑰匙以後,因為某些因素再也不能回來了,那麼這個箱子便再也不能被使用。也就是說,進入
Critical Sections線程若中途當掉了,那麼別了線程是再也進不了
Critical Sections(一個資源就這樣浪費了),那些需要進入
Critical Sections的線程會停在入口不再執行,線程永遠都結束不了。怎麼辦?(
用
WaitForSingleObject
解決)還記得上一章學過的WaitForSingleObject嗎?上一章主要用它等待線程的結束,但這個函數的作用不僅限於此,在這裡,我們再前進一小步,探究WaitForSingleObject這個函數的妙用。原型:
DWORD WaitForSingleObject ( HANDLE hObject
,DWORD time ) ;
hObject:等待對象的handle(代表一個核心對象),在上一章中代表線程handle,注意沒有,
hObject
是代表一個核心對象,也就是說,只要是核心對象,
WaitForSingleObject都能處理,處理的原則是“
當核心對象被激發時,函數返回”(有多少種核心對象?及核心對象對
激發的定義見書P76)。在這裡,我遇到了一個叫mutex核心對象,mutex對激發的定義是:
“當沒有
任何線程
擁有該mutex
,而且有一個線程
正以Wait…()
等待
該mutex
,該mutex
就會短暫地出現激髮狀態,
使Wait…()
得以返回,
那麼在其它的情況,mutex
處於未激髮狀態
”。 好了,我們又進一步的瞭解了
WaitForSingleObject函數,那麼,如何解決
Critical Sections所遇到的因難呢?當擁有mutex的
線程結束前沒有調用
ReleaseMutex(不管該線程是當了,還是忘記調用
ReleaseMutex),那麼其它正以WaitForSingleObject()等待此mutex的線程就會收到WAIT_ABANDONED_0。有了這個值,我就解開難題了。程式片段:
DWORD rc = WaitForSingleObject( hMutex ) ;
If ( rc == WAIT_OBJECT_0 )
{
//
正常的處理
}
else
{
If ( rc == WAIT_ABANDONED_0 )
{
//
沒有正常的釋放mutex
(比如擁有mutex
的線程死了)
}
} 我想,從筆記的角度來看,上面對mutex描述已經可以協助我很快的上手使用了,至於對mutex更詳細的說明,還是要看看書.。(當然,希望我的這一段描述能協助你理解mutexJ)
總結一下:
CRITICAL_SECTION Mutex
核心對象
InitializeCriticalSection() CreateMutex()
OpenMutex()
EnterCriticalSection() WaitForSingleObject()
LeaveCriticalSection() ReleaseMutex()
DeleteCriticalSection() CloseHandle()
附註:以上本文純屬加強記憶所做筆記,內容或有不詳,甚至錯誤,請閱者見諒,匆見怪!!!