用C++封裝Win32訊號量,同步執行緒
阿新 • • 發佈:2019-01-28
在Win32環境下編寫多執行緒應用程式,也會常用到訊號量Semaphore來進行執行緒同步。與其相關的一組API包括:CreateSemaphore,ReleaseSemaphore,WaitForSingleObject,和CloseHandle。關於這些API的功能以及引數意義等這裡就不多說了。下邊,我封裝了一個訊號量類,以及測試程式碼。已由本人在VS2005環境下編譯,測試通過。
MySemaphore.h
#ifndef Semaphore_Header #define Semaphore_Header #include <iostream> #include <Windows.h> #include <assert.h> using namespace std; //------------------------------------------------------------------------ class CSemaphoreImpl { protected: CSemaphoreImpl(int n, int max); ~CSemaphoreImpl(); void SetImpl(); void WaitImpl(); bool WaitImpl(long lMilliseconds); private: HANDLE m_hSema; }; inline void CSemaphoreImpl::SetImpl() { if (!ReleaseSemaphore(m_hSema, 1, NULL)) { cout<<"cannot signal semaphore"<<endl; } } //------------------------------------------------------------------------ /* 訊號量同步機制 訊號量提供一個計數值,可以進行原子操作。V 將計數值加1,使得 等待該訊號量的執行緒可以被呼叫(呼叫Set()),P 將計數值減1,使 當前執行緒被掛起,進行睡眠(呼叫Wait())。 當訊號量的計數值被初始化為0時,呼叫P操作,將掛起當前執行緒。 當訊號量被啟用,即呼叫V操作後,被掛起的執行緒就有機會被重新排程了。 */ class CMySemaphore: private CSemaphoreImpl { public: /* 建立一個訊號量,訊號量計數值當前值為引數n,最大值為max。 如果只有n,則n必須大於0;如果同時有n和max,則n必須不小 於0,且不大於max */ CMySemaphore(int n); CMySemaphore(int n, int max); /* 銷燬一個訊號量 */ ~CMySemaphore(); /* 對訊號量計數值做加1動作,訊號量變為有訊號狀態,使得 另一個等待該訊號量的執行緒可以被排程 */ void Set(); /* 對訊號量計數值做減1動作,訊號量變為無訊號狀態。若 計數值變得大於0時,訊號量才會變為有訊號狀態。 */ void Wait(); /* 在給定的時間間隔裡等待訊號量變為有訊號狀態,若成功, 則將計數值減1,否則將發生超時。 */ void Wait(long lMilliseconds); /* 在給定的時間間隔裡等待訊號量變為有訊號狀態,若成功, 則將計數值減1,返回true;否則返回false。 */ bool TryWait(long lMilliseconds); private: CMySemaphore(); CMySemaphore(const CMySemaphore&); CMySemaphore& operator = (const CMySemaphore&); }; inline void CMySemaphore::Set() { SetImpl(); } inline void CMySemaphore::Wait() { WaitImpl(); } inline void CMySemaphore::Wait(long lMilliseconds) { if (!WaitImpl(lMilliseconds)) cout<<"time out"<<endl; } inline bool CMySemaphore::TryWait(long lMilliseconds) { return WaitImpl(lMilliseconds); } #endif
MySemaphore.cpp
#include "MySemaphore.h" CSemaphoreImpl::CSemaphoreImpl(int n, int max) { assert (n >= 0 && max > 0 && n <= max); m_hSema = CreateSemaphore(NULL, n, max, NULL); if (!m_hSema) { cout<<"cannot create semaphore"<<endl; } } CSemaphoreImpl::~CSemaphoreImpl() { CloseHandle(m_hSema); } void CSemaphoreImpl::WaitImpl() { switch (WaitForSingleObject(m_hSema, INFINITE)) { case WAIT_OBJECT_0: return; default: cout<<"wait for semaphore failed"<<endl; } } bool CSemaphoreImpl::WaitImpl(long lMilliseconds) { switch (WaitForSingleObject(m_hSema, lMilliseconds + 1)) { case WAIT_TIMEOUT: return false; case WAIT_OBJECT_0: return true; default: cout<<"wait for semaphore failed"<<endl; } return false; } CMySemaphore::CMySemaphore(int n): CSemaphoreImpl(n, n) { } CMySemaphore::CMySemaphore(int n, int max): CSemaphoreImpl(n, max) { } CMySemaphore::~CMySemaphore() { }
下邊是測試程式碼
// MySem.cpp : 定義控制檯應用程式的入口點。 // #include "MySemaphore.h" #include <process.h> //建立一個訊號量,其計數值當前值為0,最大值為3 CMySemaphore g_MySem(0, 3); //執行緒函式 unsigned int __stdcall StartThread(void *pParam) { //休眠100毫秒,確保主執行緒函式main中 //建立工作執行緒下一句g_MySem.Set();先執行 Sleep(100); g_MySem.Wait(); //訊號量計數值減1 cout<<"Do print StartThread"<<endl; return (unsigned int)0; } int main(int argc, char* argv[]) { HANDLE hThread; unsigned int uiThreadId; assert ( !g_MySem.TryWait(10) ); g_MySem.Set(); //訊號量計數值加1 g_MySem.Wait(); //訊號量計數值減1 try { g_MySem.Wait(100); cout<<"must timeout"<<endl; //此處發生超時 } catch (...) { cout<<"wrong exception"<<endl; } g_MySem.Set(); g_MySem.Set(); assert ( g_MySem.TryWait(0) ); g_MySem.Wait(); assert ( !g_MySem.TryWait(10) ); //建立工作執行緒 hThread = (HANDLE)_beginthreadex(NULL, 0, &StartThread, NULL, 0, &uiThreadId); g_MySem.Set(); //等待執行緒結束 DWORD dwRet = WaitForSingleObject(hThread,INFINITE); if ( dwRet == WAIT_TIMEOUT ) { TerminateThread(hThread,0); } assert ( !g_MySem.TryWait(10) ); //若將斷言中的 ! 去掉,則會發生斷言錯誤 //關閉執行緒控制代碼,釋放資源 CloseHandle(hThread); system("pause"); return 0; }
編譯,執行
可見,在不同的執行緒,這裡是主執行緒函式main和工作執行緒函式StartThread都可以對訊號量物件g_MySem做 P操作。