一 互斥体
1 内核对象
/*
互斥对象属于内核对象,他能够确保线程拥有对单个资源的互斥访问权。互斥对象包含一个
使用数量,一个线程ID和一个计数器。其中ID用于标识系统中的那个线程当前拥有互斥对象,
计数器用于指明该线程拥有互斥对象的次数。
*/
2 接口函数
// 创建互斥体对象
HANDLE CreateMutext(
LPSECURIT_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName);
// 获取互斥体对象的所有权
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
// 释放互斥体对象
BOOL ReleaseMutex(HANDLE hMutex);
3 实例
#include <windows.h>
#include <iostream.h>
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int index = 0;
int ticktets = 100;
HANDLE hMutex;
void main()
{
HANDLE hThread1;
HANDLE hThread2;
// 创建线程
hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
// 创建互斥对象
hMutex = CreateMutex(NULL, FALSE, NULL);
Sleep(4000);
}
// 线程1的入口函数
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(TRUE)
{
WaitForSingleObject(hMutex, INFINITE);
if(ticketes > 0){
Sleep(1);
std::cout << "thread 1 sell ticket : " << tickets-- << std::endl;
}else{
// 线程一旦结束,会释放对互斥体的占用。因此,可不主动调用ReleaseMutex,来释放互斥体。
std::cout << "no tickets" << std::endl;
break;
}
ReleaseMutex(hMutex);
}
return 0;
}
// 线程2的入口函数
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(TRUE){
WaitForSingleObject(hMutex, INFINITE);
if(tickets > 0){
Sleep(1);
std::cout << "thread 2 sell ticket : " << tickets-- << std::endl;
}else{
break;
}
ReleaseMutex();
}
return 0;
}
二 事件
1 内核对象
/*
事件对象也属于内核对象,它包含以下三个成员:
1) 使用计数
2) 用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值
3) 用于指明该事件处于已通知状态还是未通知状态的布尔值
事件对象有两种不同的类型:人工重置的事件对象和自动重置的事件对象。当人工重置的事件对
象得到通知时,等待该事件对象的所有线程均变为可调度线程。当一个自动重置的事件对象得到
通知时,等待该事件对象的线程只有一个线程变为可调度线程。
*/
2 函数接口
// 创建事件
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName);
// 设置事件对象为有信号状态
BOOL SetEvent(HANDLE hEvent);
// 重置事件对象为无信号状态
BOOL ResetEvent(HANDLE hEvent);
3 实例
#include <windows.h>
#include <iostream.h>
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int tickets = 100;
HANDLE g_hEvent;
void main()
{
HANDLE hThread1;
HANDLE hThread2;
// 创建自动重置,无通知的事件。调用SetEvent,使事件处于通知状态
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
SetEvent(g_hEvent);
hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
// 让主线程睡眠4秒
Sleep(4000);
// 关闭事件对象句柄
CloseHandle(g_hEvent);
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(TRUE){
// 请求事件对象
WaitForSingleObject(g_hEvent, INFINITE);
if(tickets > 0){
Sleep(1);
std::cout << "thread1 sell tickets" << tickets-- << std::endl;
SetEvent(g_hEvent);
}else{
// 事件不和线程绑定,因此线程结束,不会释放事件。必须调用SetEvent,使事件处于通知状态。
SetEvent(g_hEvent);
break;
}
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(TRUE){
// 请求事件对象
WaitForSingleObject(g_hEvent, INFINITE);
if(tickets>0){
Sleep(1);
std::cout << "thread2 sell ticket :" << tickets-- << std::endl;
SetEvent(g_hEvent);
}else{
SetEvent(g_hEvent);
break;
}
}
return 0;
}
三 临界区
1 非内核对象
/*
临界区是非内核对象,只在用户态进行锁操作,速度快;互斥体是内核对象,在核心态进行锁操
作,速度慢。临界区只能用于对象在同一进程里线程间的互斥访问;互斥体可以用于对象进程间
或线程间的互斥访问。
*/
2 接口函数
// 初始化临界区
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
// 获取临界区对象的所有权
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
// 释放临界区对象的所有权
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
// 释放临界区
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
3 实例
#include <windows.h>
#include <iostream.h>
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int tickets = 100;
CRITICAL_SECTION g_cs;
void main()
{
HANDLE hThread1;
HANDLE hThread2;
hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
InitializeCriticalSection(&g_cs);
Sleep(4000);
DeleteCriticalSection(&g_cs);
}
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while(TRUE){
EnterCriticalSection(&g_cs);
Sleep(1);
if(tickets > 0){
Sleep(1);
std::cout << "thread 1 sell ticket : " << tickets-- << std::endl;
LeaveCriticalSection(&g_cs);
}
else{
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while(TRUE){
EnterCriticalSection(&g_cs);
Sleep(1);
if(tickets > 0){
Sleep(1);
std::cout << "thread 2 sell ticket : " << tickets-- << std::endl;
LeaveCriticalSection(&g_cs);
}
else{
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
四 互斥对象、事件对象与临界区的区别
/*
1) 互斥对象和事件对象都属于内核对象,利用内核对象进行线程同步时,速度较慢。但利用
互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。
2)关键代码段工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁
状态,因为在等待进入关键代码段时无法设定超时值。
*/