在多線程程序設計中,不可避免地面臨着同步問題。在Win32中,有以下四種同步機制。
1、臨界區 - Critical Section
(1) 說明
多線程程序中,有些代碼是共享資源,需將這些代碼作爲臨界區。如果有多個線程試圖同時訪問臨界區,那麼在一個線程進入後,其他線程將被掛起,並一直持續到進入臨界區的線程離開。臨界區在被釋放後,其他線程可以繼續搶佔。
臨界區的同步速度很快;不是內核對象,因而不能跨進程同步;不能指定阻塞時的等待時間(只能無限等待下去)。
(2) 有關函數
操作臨界區要涉及的API函數有:
InitializeCriticalSection()
EnterCriticalSection()
LeaveCriticalSection()
DeleteCriticalSection()
這四個函數的形參都是一個指向CRITICAL_SECTION結構體的指針,因而必須先定義一個CRITICAL_SECTION類型的變量。
InitializeCriticalSection()的作用是初始化一個臨界資源對象;EnterCriticalSection()的作用是查看CRITICAL_SECTION結構成員變量的值,判斷是否有線程訪問臨界區的資源。如果沒有,則更新CRITICAL_SECTION結構成員變量的值,並將當前的線程賦予資源訪問權;如果有線程正在訪問臨界區的資源,則該函數將線程置爲等待狀態;LeaveCriticalSection()釋放臨界區資源的所有權,使其他等待臨界區資源的線程能夠有機會獲得臨界區資源的所有權。
(3) 應用
#ifndef _ZCZ_WIN32_TOOLS_CCRITICALSECTION_H_
#define _ZCZ_WIN32_TOOLS_CCRITICALSECTION_H_
#include <windows.h>
namespace zcz_win32_tools
{
class CCriticalSection
{
public:
CCriticalSection();
~CCriticalSection();
public:
void EnterCriticalSection();
void LeaveCriticalSection();
private:
CRITICAL_SECTION m_ObjCriticalSection;
};
class CCriticalSectionOwner
{
public:
CCriticalSectionOwner(CCriticalSection &);
~CCriticalSectionOwner();
private:
CCriticalSection &m_refCCriticalSection;
};
}
#endif
#include "./CCriticalSection.h"
namespace zcz_win32_tools
{
CCriticalSection::CCriticalSection()
{
::InitializeCriticalSection(&m_ObjCriticalSection);
}
CCriticalSection::~CCriticalSection()
{
::DeleteCriticalSection(&m_ObjCriticalSection);
}
void CCriticalSection::EnterCriticalSection()
{
::EnterCriticalSection(&m_ObjCriticalSection);
}
void CCriticalSection::LeaveCriticalSection()
{
::LeaveCriticalSection(&m_ObjCriticalSection);
}
CCriticalSectionOwner::CCriticalSectionOwner(CCriticalSection &ObjCCriticalCestion)
:m_refCCriticalSection(ObjCCriticalCestion)
{
m_refCCriticalSection.EnterCriticalSection();
}
CCriticalSectionOwner::~CCriticalSectionOwner()
{
m_refCCriticalSection.LeaveCriticalSection();
}
}
2、互斥量 - Mutex
(1) 說明
互斥對象的作用是保證每次只能有一個線程獲得互斥對象而得以繼續執行。互斥對象主要包含使用數量、線程ID和遞歸計數器等信息。其中,線程ID表示當前擁有互斥對象的線程號,遞歸計數器表示線程擁有互斥對象的次數。
互斥對象是是Windows的內核對象,可跨進程互斥,並且能指定阻塞時的等待時間。
(2) 有關函數
使用互斥對象要涉及的API函數主要有:
CreateMutex() // 創建互斥對象
ReleaseMutex() // 釋放互斥對象
OpenMutex() // 跨進程時使用
WaitForSingleObject() // 等待指定時間
使用互斥編程的一般方法是:
void UpdateResource
{
WaitForSingleObject(hMutex,...);
// do something...
ReleaseMutex(hMutex);
}
(3) 應用
#include <windows.h>
#include <iostream>
using namespace std;
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int tickets=100;
HANDLE hMutex=CreateMutex(NULL,FALSE,NULL);
void main()
{
HANDLE hThread1,hThread2;
hThread1=::CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
hThread2=::CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
::CloseHandle(hThread1);
::CloseHandle(hThread2);
::Sleep(INFINITE);
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hMutex,INFINITE);
if (tickets>0)
{
cout<<"t1: "<<tickets--<<endl;
}
else
{
break;
}
ReleaseMutex(hMutex);
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while (1)
{
WaitForSingleObject(hMutex,INFINITE);
if (tickets>0)
{
cout<<"t2: "<<tickets--<<endl;
}
else
{
break;
}
ReleaseMutex(hMutex);
}
return 0;
}
3、事件 - Event
(1)說明
事件是內核對象,具有“激發狀態”和“未激發狀態”兩種狀態。事件主要分爲兩類:
人工重置事件:用程序手動設置。
自動重置事件:一旦事件發生並被處理後,自動恢復到沒有時間狀態。
(2)有關函數
使用使用事件對象要涉及的API函數主要有:
CreateEvent() // 創建事件對象
SetEvent() // 設置事件對象
ResetEvent() // 設置事件對象
PulseEvent() // 設置事件對象
OpenEvent() // 跨進程時使用
WaitforSingleEvent() // 等待
WaitForMultipleObjects()// 等待
(3)應用
#ifndef _ZCZ_WIN32_TOOLS_CEVENT_H_
#define _ZCZ_WIN32_TOOLS_CEVENT_H_
#include <Windows.h>
namespace zcz_win32_tools
{
class CEvent
{
public:
CEvent();
~CEvent();
public:
BOOL SetEvent();
BOOL ResetEvent();
BOOL WaitInfinite();
private:
HANDLE m_hEvent;
};
}
#endif
#include "./CEvent.h"
namespace zcz_win32_tools
{
CEvent::CEvent():m_hEvent(NULL)
{
m_hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}
CEvent::~CEvent()
{
}
BOOL CEvent::SetEvent()
{
if (NULL == m_hEvent || 0 == ::SetEvent(m_hEvent))
{
return FALSE;
}
return TRUE;
}
BOOL CEvent::ResetEvent()
{
if (NULL == m_hEvent || 0 == ::ResetEvent(m_hEvent) )
{
return FALSE;
}
return TRUE;
}
BOOL CEvent::WaitInfinite()
{
if ( WAIT_OBJECT_0 == ::WaitForSingleObject(m_hEvent, INFINITE) )
{
return TRUE;
}
return FALSE;
}
}
4、信號量 - Semaphore
(1)說明
信號量允許多個線程在同一時刻訪問統一資源,但是限制了在同一時刻訪問共享資源的最大線程數。
信號量是內核對象,允許跨進程使用。
(2)有關函數
使用信號量要涉及的API函數主要有:
CreateSemaphore() // 創建信號量
ReleaseSemaphore() // 釋放信號量
OpenSemaphore() // 跨進程使用
在用 CreateSemaphore()創建信號量時即要同時指出允許的最大資源計數和當前可用資源計數。一般是將當前可用資源計數設置爲最大資源計數,每增加一個線程對共享資源的訪問,當前可用資源計數就會減1,只要當前可用資源計數是大於0的,就可以發出信號量信號。但是當前可用計數減小到0時則說明當前佔用資源的線程數已經達到了所允許的最大數目,不能在允許其他線程的進入,此時的信號量信號將無法發出。線程在處理完共享資源後,應在離開的同時通過 ReleaseSemaphore()函數將當前可用資源計數加1。在任何時候當前可用資源計數決不可能大於最大資源計數。