基于上篇的多線程編程的基本內(nèi)容,本篇開(kāi)始Windows操作系統(tǒng)下各種常用的多線程資源同步對(duì)象婿失。
(1)windows線程資源同步之臨界區(qū):兩個(gè)重要的 Windows API 函數(shù) WaitForSingleObject 和 WaitForMultipleObjects辟躏,前者是一次操作一個(gè)資源同步對(duì)象辆憔,后者同時(shí)操作多個(gè)資源
同步對(duì)象渣蜗,區(qū)別和注意點(diǎn)如下:
? ? ? ? ? ? ?? 1.DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); //參數(shù)hhandle表示需要等待的內(nèi)核對(duì)象,參數(shù)dwMilliseconds 實(shí)際上是一個(gè)?unsigned long 類
型县遣,設(shè)置 INFINITE 表示無(wú)限等待糜颠,在 Windows 上可以調(diào)用WaitForSingleObject等待的常見(jiàn)對(duì)象如下表所示:
? ? ? ? ? ? ? ? ? ? ?? 可以被等待的對(duì)象 ? ? ? ? ? ? ? ?? 等待對(duì)象成功的含義 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 對(duì)象類型
? ? ? ? ? ? ? ? ? ? ? ? 線程 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 等待線程結(jié)束 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? HANDLE
? ? ? ? ? ? ? ? ? ? ?? Process ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 等待進(jìn)程結(jié)束 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? HANDLE
? ? ? ? ? ? ? ? ? ? ?? Event(事件) ? ? ? ? ? ? ? ? ? ? ?? 等待 Event 有信號(hào) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? HANDLE
? ? ? ? ? ? ? ? ? ? ?? Mutex (互斥體) ? ? ? ? ? ? ? ? ? ? ? 等待持有 Mutex 的線程釋放該 Mutex汹族,等待成功,擁有該 Mutex ? ? HANDLE
? ? ? ? ? ? ? ? ? ? ?? Semaphore(信號(hào)量) ? ? ? ?? 等待該 Semaphore 對(duì)象有信號(hào) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? HANDLE
? ? ? ? ? ?? 函數(shù)的返回類型有:WAIT_FAILED -調(diào)用失敗其兴,可以用GetLastError()得到具體的錯(cuò)誤碼顶瞒,WAIT_OBJECT_0-表示成功等待到設(shè)置的對(duì)象,WAIT_TIMEOUT-等待超時(shí)
WAIT_ABANDONED-當(dāng)?shù)却膶?duì)象是 Mutex 類型時(shí)元旬,如果持有該 Mutex 對(duì)象的線程已經(jīng)結(jié)束榴徐,但是沒(méi)有在結(jié)束前釋放該 Mutex,此時(shí)該 Mutex 已經(jīng)處于廢棄狀態(tài)匀归,其行為是未知的坑资,
不建議再使用。
? ? ? ? ? ? 2. DWORD WaitForMultipleObjects( DWORD nCount,const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);? //nCount 指定對(duì)象數(shù)組的長(zhǎng)度穆端,lpHandles表示需要
等待的對(duì)象數(shù)組指針袱贮,bWaitAll表示是否等待所有對(duì)象數(shù)組有信號(hào),可設(shè)置true或者false体啰,設(shè)置false只要有一個(gè)對(duì)象有信號(hào)即會(huì)返回攒巍,在設(shè)置bWaitAll 為false的情況下, 除了和上述介紹
的返回值是 WAITFAILED和荒勇,柒莉,WAITTIMEOUT 以外,由于是多個(gè)對(duì)象同步沽翔,返回值 WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1)兢孝,比如:現(xiàn)在等待三個(gè)對(duì)象 A1、A2搀擂、A3西潘,
它們?cè)跀?shù)組 lpHandles 中的下標(biāo)依次是 0、1哨颂、2,某次 WaitForMultipleObjects 返回值是 Wait_OBJECT_0 + 1相种,則表示對(duì)象 A2 有信號(hào)威恼,導(dǎo)致 WaitForMultipleObjects 調(diào)用成功返回。同
理對(duì)于WAIT_ABANDONED_0 to (WAIT_ABANDONED_0 + nCount– 1)寝并。
? ? ? ? ?? windows的臨界區(qū)對(duì)象操作API函數(shù):
? ? ? ? ? void ?? InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);? //初始化臨界區(qū)對(duì)象
? ? ? ? ? void ?? DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);? //銷毀臨界區(qū)對(duì)象
? ? ? ? ? BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); //嘗試進(jìn)入臨界區(qū)箫措,如果是,返回true衬潦,無(wú)法進(jìn)入則阻塞斤蔓,返回false
? ? ? ? ? void ? ? EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);? //進(jìn)入臨界區(qū)
? ? ? ? ? void ? ? LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); //離開(kāi)臨界區(qū)
? ? ? ?? 注意:此臨界區(qū)對(duì)象是互斥的箭跳,只能一個(gè)線程訪問(wèn)持有漂彤,為了避免死鎖慨削,進(jìn)入和離開(kāi)臨界區(qū)函數(shù)需要成對(duì)使用旨涝,為了避免因函數(shù)有多個(gè)出口造成的編碼疏漏,可使用 RAII 封裝臨界
區(qū)對(duì)象類的方法驾锰,此API函數(shù)為Windows 系統(tǒng)多線程資源同步最常用的對(duì)象之一卸留。
(2)windows線程資源同步之Event:Event是windows內(nèi)核的常用多線程同步的對(duì)象,特點(diǎn)是簡(jiǎn)單易用椭豫,但無(wú)法精確控制喚醒指定數(shù)量的線程耻瑟。API函數(shù) 為:
? ? ? ?? HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL ? bManualReset, BOOL? bInitialState,LPCTSTR ? lpName); ?? //參數(shù)lpEventAttributes 設(shè)置Event對(duì)象
的安全屬性,設(shè)置NULL為默認(rèn)安全屬性赏酥;參數(shù)bManualReset表示設(shè)置 Event 對(duì)象受信(變成有信號(hào)狀態(tài))時(shí)的行為喳整,設(shè)置true要手動(dòng)調(diào)用 ResetEvent 函數(shù)去將 Event 重置成無(wú)信號(hào)
狀態(tài),設(shè)置false表示Event 事件對(duì)象受信后會(huì)自動(dòng)重置為無(wú)信號(hào)狀態(tài)裸扶;參數(shù) bInitialState 設(shè)置 Event 事件對(duì)象初始狀態(tài)是否是受信的算柳,true表示有信號(hào),false表示無(wú)信號(hào)姓言;參數(shù) lpName?
可以設(shè)置 Event 對(duì)象的名稱瞬项,可設(shè)置為NULL,Event對(duì)象可以通過(guò)名稱在進(jìn)程之前不同進(jìn)程之間共享何荚;返回值為 NULL表示創(chuàng)建失敗囱淋。
? ? ? ? ? ? BOOL? SetEvent(HANDLE hEvent); //設(shè)置Event對(duì)象從無(wú)信號(hào)變成有信號(hào)狀態(tài)
? ? ? ? ? ? BOOL? ResetEvent(HANDLE hEvent); //設(shè)置Event對(duì)象從有信號(hào)變成無(wú)信號(hào)狀態(tài)
(3)windows線程同步之Mutex:windows的Mutex(互斥量)在同一時(shí)刻只能屬于一個(gè)線程,也可以不屬于任何線程餐塘,具有排他性妥衣,創(chuàng)建Mutex可設(shè)置它屬于的線程,其他線程要獲取調(diào)
用WaitForSingleObject 進(jìn)行申請(qǐng)戒傻,創(chuàng)建 Mutex 的 API 是 CreateMutex税手,釋放 Mutex 的 API 是ReleaseMutex :
? ? ? ? ?? HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL? bInitialOwner, LPCTSTR? lpName);? //參數(shù) lpMutexAttributes 與Event函數(shù)類似,設(shè)置?
lpMutexAttribute為NULL需纳,參數(shù) bInitialOwner表示調(diào)用創(chuàng)建Mutex的線程是否立即擁有該對(duì)象芦倒,true為擁有,fasle為不擁有不翩;參數(shù) lpName為Mutex 對(duì)象的名稱兵扬,和Event對(duì)象一樣,多個(gè)
線程之間可通過(guò)名稱共享口蝠;返回值為NULL表示創(chuàng)建失斊髦印;
? ? ? ? ?? BOOL ReleaseMutex(HANDLE hMutex); //? 參數(shù) hMutex 即需要釋放所有權(quán)的 Mutex 對(duì)象句柄
(4)windows線程同步之Semaphore:Semaphore 也是 Windows 多線程同步常用的對(duì)象之一妙蔗,與Event傲霸、Mutex區(qū)別不同的是可以信號(hào)量存在資源計(jì)數(shù),可以精確控制喚醒的線程數(shù)
目。創(chuàng)建 Semaphore 對(duì)象的 API 函數(shù)和增加信號(hào)量資源個(gè)數(shù)的API如下所示:
? ? ? ? ? HANDLE? CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG? lInitialCount, LONG lMaximumCount,LPCTSTR ? lpName);? //參數(shù)?
lpSemaphoreAttributes 指定對(duì)象的安全屬性昙啄,設(shè)置為NULL;參數(shù) lInitialCount 指定初始可用資源數(shù)量,每調(diào)用一次 WaitForSingleObject 獲得 Semaphore 對(duì)象穆役,該對(duì)象的資源計(jì)數(shù)會(huì)
減少一個(gè),參數(shù) lMaximumCount 最大資源數(shù)量上限跟衅,設(shè)置大于0孵睬,使用ReleaseSemaphore 增加資源個(gè)數(shù)不能大于這個(gè)上限值;參數(shù) lpName 指定 Semaphore 對(duì)象的名稱伶跷,可通過(guò)
名稱進(jìn)行跨進(jìn)程共享掰读;返回值為NULL表示創(chuàng)建失敗。
? ? ? ? ? BOOL? ReleaseSemaphore( HANDLE hSemaphore,LONG? lReleaseCount,? LPLONG lpPreviousCount);? //新增信號(hào)量的資源個(gè)數(shù)個(gè)數(shù)叭莫,參數(shù) hSemaphore 是需要操作的信號(hào)量
句柄蹈集;參數(shù) lReleaseCount,需要增加的資源數(shù)量雇初;參數(shù) lpPreviousCount 是一個(gè) long 型(32 位系統(tǒng)上 4 個(gè)字節(jié))的指針拢肆,返回上一次資源的數(shù)量。
? ? ? ? 信號(hào)量根據(jù)當(dāng)前資源的個(gè)數(shù)分配消費(fèi)者靖诗,當(dāng)資源數(shù)量為0時(shí)郭怪,所有消費(fèi)者處于掛起狀態(tài),當(dāng)新資源到來(lái)時(shí)刊橘,消費(fèi)者會(huì)被喚醒鄙才。
(5)讓程序只啟動(dòng)一個(gè)實(shí)例的方法:使用線程內(nèi)核同步對(duì)象,首次啟動(dòng)這個(gè)進(jìn)程促绵,這個(gè)進(jìn)程會(huì)調(diào)用 CreateMutex 函數(shù)創(chuàng)建一個(gè)名稱為“MySingleInstanceApp”的互斥體對(duì)象攒庵。當(dāng)再次準(zhǔn)備
啟動(dòng)一份這個(gè)進(jìn)程時(shí),再次調(diào)用 CreateMutex 函數(shù)败晴,由于該名稱的互斥體對(duì)象已經(jīng)存在浓冒,將會(huì)返回已經(jīng)存在的互斥體對(duì)象地址,此時(shí)通過(guò) GetLastError() 函數(shù)得到的錯(cuò)誤碼是?
ERROR_ALREADY_EXISTS 表示該名稱的互斥體對(duì)象已經(jīng)存在尖坤,此時(shí)我們激活已經(jīng)存在的前一個(gè)實(shí)例稳懒,然后退出當(dāng)前進(jìn)程即可。如下函數(shù)實(shí)現(xiàn):
? ? ? ? ? bool CheckInstance()
? ? ? ? {
? ? ? ? ? ? ?? HANDLE hSingleInstanceMutex = CreateMutex(NULL, FALSE, _T("MySingleInstanceApp"));
? ? ? ? ? ? ? if (hSingleInstanceMutex != NULL)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? if (GetLastError() == ERROR_ALREADY_EXISTS)
? ? ? ? ? ? ? ? ?? {
? ? ? ? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? ? }
? ? ? ? ?? }
? ? ? ? ?? return false;
? ?? }?