临界区是一种最简单的同步对象,它只可以在同一进程内部使用。它的作用是保证只有一个线程可以申请到该对象 |
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection ); |
产生临界区 |
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection ); |
删除临界区 |
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection ); |
进入临界区,相当于申请加锁,如果该临界区正被其他线程使用则该函数会等待到其他线程释放 |
bool TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection ); |
进入临界区,相当于申请加锁,和EnterCriticalSection不同如果该临界区正被其他线程使用则该函数会立即返回FALSE,而不会等待 |
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection ); |
退出临界区,相当于申请解锁 |
下面的示范代码演示了如何使用临界区来进行数据同步处理:
//全局变量
int iCounter=0;
CRITICAL_SECTION criCounter;
DWORD threadA(void* pD)
{
int iID=(int)pD;
for(int i=0;i<8;i++)
{
EnterCriticalSection(&criCounter);
int iCopy=iCounter;
Sleep(100);
iCounter=iCopy+1;
printf("thread %d : %dn",iID,iCounter);
LeaveCriticalSection(&criCounter);
}
return 0;
}
//in main function
{
//创建临界区
InitializeCriticalSection(&criCounter);
//创建线程
HANDLE hThread[3];
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)threadA,(void*)1);
CWinThread* pT2=AfxBeginThread((AFX_THREADPROC)threadA,(void*)2);
CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)threadA,(void*)3);
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
//等待线程结束
//至于 WaitForMultipleObjects 的用法后面会讲到。
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
//删除临界区
DeleteCriticalSection(&criCounter);
printf("novern");
}
互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它
创建互斥量:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全信息
BOOL bInitialOwner, // 最初状态,
//如果设置为真,则表示创建它的线程直接拥有了该互斥量,而不需要再申请
LPCTSTR lpName // 名字,可以为NULL,但这样一来就不能被其他线程/进程打开
);
打开一个存在的互斥量:
HANDLE OpenMutex(
DWORD dwDesiredAccess, // 存取方式
BOOL bInheritHandle, // 是否可以被继承
LPCTSTR lpName // 名字
);
释放互斥量的使用权,但要求调用该函数的线程拥有该互斥量的使用权:
BOOL ReleaseMutex( //作用如同LeaveCriticalSection
HANDLE hMutex // 句柄
);
关闭互斥量:
BOOL CloseHandle(
HANDLE hObject // 句柄
);
对于互斥量来讲如果正在被使用则为无信号状态,被释放后变为有信号状态。当等待成功后 WaitForSingleObject 函数会将互斥量置为无信号状态,这样其他的线程就不能获得使用权而需要继续等待。WaitForSingleObject 函数还进行排队功能,保证先提出等待请求的线程先获得对象的使用权
int iCounter=0;
DWORD threadA(void* pD)
{
int iID=(int)pD;
//在内部重新打开
HANDLE hCounterIn=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"sam sp 44");
for(int i=0;i<8;i++)
{
printf("%d wait for objectn",iID);
WaitForSingleObject(hCounterIn,INFINITE);
int iCopy=iCounter;
Sleep(100);
iCounter=iCopy+1;
printf("ttthread %d : %dn",iID,iCounter);
ReleaseMutex(hCounterIn);
}
CloseHandle(hCounterIn);
return 0;
}
//in main function
{
//创建互斥量
HANDLE hCounter=NULL;
if( (hCounter=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"sam sp 44"))==NULL)
{
//如果没有其他进程创建这个互斥量,则重新创建
hCounter = CreateMutex(NULL,FALSE,"sam sp 44");
}
//创建线程
HANDLE hThread[3];
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)threadA,(void*)1);
CWinThread* pT2=AfxBeginThread((AFX_THREADPROC)threadA,(void*)2);
CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)threadA,(void*)3);
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
//等待线程结束
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
//关闭句柄
CloseHandle(hCounter);
}
}
WaitForSingleObject 这个函数可以作用于:
Mutex
Event
Semaphore
Job
Process
Thread
Waitable timer
Console input
互斥量(Mutex),信号灯(Semaphore),事件(Event)都可以被跨越进程使用来进行同步数据操作,而其他的对象与数据同步操作无关,但对于进程和线程来讲,如果进程和线程在运行状态则为无信号状态,在退出后为有信号状态。所以我们可以使用 WaitForSingleObject 来等待进程和线程退出。(至于信号灯,事件的用法我们接下来会讲)我们在前面的例子中使用了 WaitForMultipleObjects 函数,这个函数的作用与 WaitForSingleObject 类似但从名字上我们可以看出,WaitForMultipleObjects 将用于等待多个对象变为有信号状态,函数原型如下:
DWORD WaitForMultipleObjects(
DWORD nCount, // 等待的对象数量
CONST HANDLE *lpHandles, // 对象句柄数组指针
BOOL fWaitAll, // 等待方式,
//为TRUE表示等待全部对象都变为有信号状态才返回,为FALSE表示任何一个对象变为有信号状态则返回
DWORD dwMilliseconds