Windows编程-线程-线程同步2

也是游戏防止多开的原理

事件

事件(Event)是在线程同步中最常使用的一种同步对象,事件包含一个使用计数,一个是用来表示自动重置/手动重置的布尔值,另一个是表示事件有没有触发的布尔值。

事件对象有两种状态:1、手动状态。2、自动状态

手动状态事件对象的激发态和非激发态是由我们来控制,自动状态和互斥体类似

事件也是一个内核对象

创建事件对象-CreateEvent

HANDLE CreateEventA(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL                 bManualReset,//如果此参数为TRUE,则该函数将创建一个手动重置的事件对象,该对象需要使用 ResetEvent函数将事件状态设置为非信号状态。如果此参数为FALSE,则该函数将创建一个自动重置事件对象,并且在释放单个等待线程之后,系统会自动将事件状态重置为非信号状态。
BOOL                 bInitialState,//如果为True就表示有信号状态,如果false就表示无信号状态,也就是一个初始状态
LPCSTR               lpName//事件的名字
);

例子:

HANDLE hEvent;
void Create_MyEvent()
{
hEvent = CreateEvent(NULL,FALSE,TRUE,L"Sna1lGo");
}

调用事件Event

WaitForsingleObject()

SetEvent()---将指定的事件对象设置为信号状态。

等待事件信号,然后用完之后重置事件

DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
for (int i = 0; i < 100; i++)
{
WaitForSingleObject(hEvent, -1);
Sum_Count++;
SetEvent(hEvent);
}
return 0;
}

事件对象没有拥有者的概念,谁都可以设置

利用事件来控制线程顺序

在创建线程的时候就先设置为无信号状态,然后有多少个线程就建立多少个事件,在自己想要的顺序中,依次设置信号,然后再唤醒下一个要执行的信号来操作。

//例子
#include<Windows.h>
#include<iostream>
HANDLE hEvent1;
HANDLE hEvent2;
HANDLE hEvent3;

LONG Sum_Count = 0;
void Create_MyEvent()
{
hEvent1 = CreateEvent(NULL,FALSE,TRUE,L"Sna1lGo");
hEvent2 = CreateEvent(NULL, FALSE, FALSE, L"Sna1lGo");
hEvent3 = CreateEvent(NULL, FALSE, FALSE, L"Sna1lGo");

}
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
WaitForSingleObject(hEvent1, -1);
printf("Hello ,this is Thread1\n");
SetEvent(hEvent2);
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
WaitForSingleObject(hEvent2, -1);
printf("Hello ,this is Thread2\n");
SetEvent(hEvent3);
return 0;
}
DWORD WINAPI ThreadProc3(LPVOID lpParameter)
{
WaitForSingleObject(hEvent3, -1);
printf("Hello ,this is Thread3\n");
return 0;
}

int main()
{
HANDLE hThread1 = CreateThread(NULL,NULL,ThreadProc1,NULL,0,NULL);
HANDLE hThread2 = CreateThread(NULL,NULL,ThreadProc2,NULL,0,NULL);
HANDLE hThread3 = CreateThread(NULL, NULL, ThreadProc3, NULL, 0, NULL);

WaitForSingleObject(hThread1, -1);
WaitForSingleObject(hThread2, -1);
WaitForSingleObject(hThread3, -1);
printf("Sum_Count=%ld\n", Sum_Count);

CloseHandle(hThread1);
CloseHandle(hThread2);
CloseHandle(hThread3);
return 0;
}

信号量

信号量也没有拥有者的概念,但是它有数量

信号量有一个当前信号数,只要这个数不为0,信号量就处于激发态

当有线程调用WaitForSingleObject后,信号数减1,如果不为0的话,再有线程调用WaitForSingleObject会继续上一把锁。相反调用ReleaseSemaphoore会将信号量增1。如果信号量为0,当有线程调用WaitForSingleObject时,线程会被阻塞。

使用场景,多开数量检查

创建信号量-CreateSemaphore

HANDLE CreateSemaphoreW(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG                 lInitialCount,//信号量对象的初始计数
LONG                 lMaximumCount,//信号量对象的最大计数
LPCWSTR               lpName//信号量对象的名称
);

释放信号量-ReleaseSemaphore

将指定的信号量对象的计数增加指定的数量。

 

BOOL ReleaseSemaphore(
HANDLE hSemaphore,//信号量的句柄,由Create或者openSemaphore产生的
LONG   lReleaseCount,//信号量要增加的数量
LPLONG lpPreviousCount//指向变量的指针,以接收信号量的先前计数。如果不需要上一个计数,则此参数可以为NULL。
);

如果函数成功,则返回值为非零。

通过信号量来限制游戏多开

#include<Windows.h>
#include<iostream>
HANDLE hSemaphore;

int main()
{
hSemaphore = CreateSemaphore(NULL,0,3,L"SnailGo");
//创建信号量,最多三个,初始值为1个
BOOL Ret_Success = ReleaseSemaphore(hSemaphore,1,NULL);//调用一次添加一个信号量
if (Ret_Success == 0)
{
MessageBox(NULL, L"只能创建三个应用程序", L"创建失败", MB_OK);
exit(0);
}
system("pause");
return 0;
}

信号量总结

灵活应用信号量来限制多开