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。在任何时候当前可用资源计数决不可能大于最大资源计数。