线程之间的同步(书接上文)
4 Semaphores 信号量
-
系统核心对象
-
与Mutex的区别在于
- Mutex: 同一时间内只能有一个线程拥有操作的权力
- Semaphores: 同一时间内可以有多个线程获得目标并操作…当然了,如果设置Semaphores的最大可获取对象为1的话,基本就等价于一个Mutex了
-
使用方法(类似于Mutex)
1. 创建一个信号量:CreateSemaphore
HANDLE CreateSemaphoreA(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount, // 初始时,信号量的可用状态,大于0时,信号量可用
LONG lMaximumCount, // 信号量可用的最大数目
LPCSTR lpName
);
2. 打开一个已经存在的信号量:OpenSemaphore
3. 获得信号量的一个占有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数
4. 释放信号量的占有权: ReleaseSemaphore;
5. 关闭信号量:CloseHandle;
- 特点:
// Semaphores 可以跨进程使用,所以其名称对整个系统而言是全局的.所以命名不要过于普通
// 在“等待”一个 信号量 的时候,可以指定“结束等待”的时间长度;
// 执行效率相比用户对象慢一些
5 Event Objects 事件
- Event 方式是最具弹性的同步机制,因为他的状态完全由你去决定,不会像 Mutex 和 Semaphores 的状态会由类似:WaitForSingleObject 一类的函数的调用而改变。互斥器和信号量的使用中,WaitForSingleObject等一类函数如果成功接收到指定的对象,那么互斥器和信号量的状态即发生改变
- 使用方法:
1、创建一个事件对象:CreateEvent;
HANDLE CreateEventA(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset, // 手动:需要手动设置事件的状态,Wait...之后需要使用ResetEvent函数去设置事件状态位无信号的(未激发的)比较灵活
// 自动:计算机自动设置,Wait...函数执行后,自动变为无信号状态,类似Mutex这样
BOOL bInitialState, // 初始状态
LPCSTR lpName
);
2、打开一个已经存在的事件对象:OpenEvent;
3、获得事件的占有权:WaitForSingleObject 等函数(可能造成阻塞);
4、释放事件的占有权(设置为激发(有信号)状态,以让其他等待中的线程苏醒):SetEvent;
5、手动置为非激发(无信号)状态:ResetEvent
6、关闭事件对象句柄:CloseHandle;
- 特点
1、系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、通常被用于 overlapped I/O (重叠IO)或被用来设计某些自定义的同步对象。
代码示例:
// 不要有恐惧心理,代码虽长,逻辑简单,静心看完,必有所获!!
#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 4
HANDLE ghGlobalWriteEvent; //全局写事件
HANDLE ghReadEvents[THREADCOUNT]; //全局读事件数组
DWORD WINAPI ThreadProc(LPVOID); //线程处理函数的函数声明
// 创建事件和线程函数(主函数在下方)
void CreateEventsAndThreads(void)
{
HANDLE hThread;
DWORD i, dwThreadID;
// ==================================================================================
// Create a manual-reset event object. The master thread sets
// this to nonsignaled when it writes to the shared buffer.
// 创建一个事件对象
ghGlobalWriteEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
TRUE, // initial state is signaled
TEXT("WriteEvent") // object name
);
if (ghGlobalWriteEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError()); // 创建失败
return;
}
else if ( GetLastError() == ERROR_ALREADY_EXISTS ) // 已经存在
{
printf("Named event already exists.\n");
return;
}
// ====================================================================================
// Create multiple threads and an auto-reset event object
// for each thread. Each thread sets its event object to
// signaled when it is not reading from the shared buffer.
// 创建多线程,并且为每一个线程都创建一个自动模式的事件
for(i = 0; i < THREADCOUNT; i++)
{
// Create the auto-reset event
ghReadEvents[i] = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
TRUE, // initial state is signaled
NULL); // object not named
if (ghReadEvents[i] == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError()); // 创建失败
return;
}
// 创建线程,并将事件句柄作为线程参数
hThread = CreateThread(NULL,
0,
ThreadProc,
&ghReadEvents[i], // pass event handle
0,
&dwThreadID);
if (hThread == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
}
}
// 向Buffer中写数据的函数
void WriteToBuffer(VOID)
{
DWORD dwWaitResult, i;
// 先将全局写事件设为无信号,目的是阻止别的线程此时往里面写数据(也就达到了线程同步的目的)
if (! ResetEvent(ghGlobalWriteEvent) )
{
printf("ResetEvent failed (%d)\n", GetLastError()); //设置失败
return;
}
// Wait for all reading threads to finish reading
// 然后等待所有的读Buffer线程完成任务
dwWaitResult = WaitForMultipleObjects(
THREADCOUNT, // number of handles in array
ghReadEvents, // array of read-event handles
TRUE, // wait until all are signaled
INFINITE); // indefinite wait一直等待
switch (dwWaitResult)
{
// All read-event objects were signaled
case WAIT_OBJECT_0:
// TODO: Write to the shared buffer
printf("Main thread writing to the shared buffer...\n");
break;
// An error occurred
default:
printf("Wait error: %d\n", GetLastError());
ExitProcess(0);
}
// Set ghGlobalWriteEvent to signaled
// 写完数据之后,将全局写事件重新设置为有信号
if (! SetEvent(ghGlobalWriteEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitProcess(0);
}
// Set all read events to signaled
for(i = 0; i < THREADCOUNT; i++)
if (! SetEvent(ghReadEvents[i]) )
{
printf("SetEvent failed (%d)\n", GetLastError());
return;
}
}
void CloseEvents()
{
int i;
for( i=0; i < THREADCOUNT; i++ )
CloseHandle(ghReadEvents[i]);
CloseHandle(ghGlobalWriteEvent);
}
// 主函数在这里啊!!!!!!!!!!!!!!!
void main()
{
int i;
// TODO: Create the shared buffer
// Create the events and THREADCOUNT threads to read from the buffer
CreateEventsAndThreads();
// Write to the buffer three times, just for test purposes
for(i=0; i < 3; i++)
WriteToBuffer();
// Close the events
CloseEvents();
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
DWORD dwWaitResult;
HANDLE hEvents[2];
hEvents[0] = *(HANDLE*)lpParam; // thread's read event
hEvents[1] = ghGlobalWriteEvent; // global write event
dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
hEvents, // array of event handles
TRUE, // wait till all are signaled
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// Both event objects were signaled
case WAIT_OBJECT_0:
// TODO: Read from the shared buffer
printf("Thread %d reading from buffer...\n",
GetCurrentThreadId());
break;
// An error occurred
default:
printf("Wait error: %d\n", GetLastError());
ExitThread(0);
}
// Set the read event to signaled
if (! SetEvent(hEvents[0]) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
return 1;
}
// 节选自微软官网