C++ 经典线程同步 事件Event(九)说明了event的原理
下面给个多线程案例使用event
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <process.h>
#include <stdio.h>
struct ThreadInfo
{
ThreadInfo();
~ThreadInfo();
HANDLE fire;
HANDLE stop;
private:
ThreadInfo(const ThreadInfo &);
ThreadInfo& operator=(const ThreadInfo &);
};
ThreadInfo::ThreadInfo()
{
fire = CreateEvent(0, FALSE, FALSE, NULL);
stop = CreateEvent(0, TRUE, FALSE, NULL);
}
ThreadInfo::~ThreadInfo()
{
CloseHandle(stop);
CloseHandle(fire);
}
unsigned __stdcall threadfunc(void* param)
{
if (ThreadInfo* info = reinterpret_cast<ThreadInfo*>(param))
{
char buf[32];
for (;;)
{
HANDLE h[2] = { info->fire, info->stop };
DWORD ret = WaitForMultipleObjects(2, h, FALSE, INFINITE);
if (ret == WAIT_OBJECT_0)
{
_snprintf(buf, sizeof(buf), "Thread %ld fired\n", GetCurrentThreadId());
printf(buf);
}
else if (ret == WAIT_OBJECT_0+1)
{
_snprintf(buf, sizeof(buf), "Thread %ld clean shutdown\n", GetCurrentThreadId());
printf(buf);
break;
}
else
{
_snprintf(buf, sizeof(buf), "Thread %ld abnormal shutdown\n", GetCurrentThreadId());
printf(buf);
break;
}
}
}
return 0;
}
int main()
{
const size_t N = 5;
ThreadInfo info[N];
HANDLE h[N];
for (size_t i = 0; i != N; ++i)
{
unsigned threadid;
h[i] = (HANDLE)_beginthreadex(0, 0, threadfunc, &info[i], 0, &threadid);
}
Sleep(5*1000);
for (size_t i = 0; i != N; ++i)
SetEvent(info[i].fire);
Sleep(5*1000);
for (size_t i = 0; i != N; ++i)
SetEvent(info[i].stop);
Sleep(5*1000);
system("pause");
return 0;
}
运行效果:
解析上篇博客中的案例(用事件Event来尝试解决这个线程同步问题)。
#include<iostream>
#include <process.h>
#include <windows.h>
using namespace std;
long g_nNum;
unsigned int __stdcall Fun(PVOID pPM);
const int THREAD_NUM = 10;
//事件与关键段
HANDLE g_hThreadEvent;
CRITICAL_SECTION g_csThreadCode;
int main()
{
cout<<" 经典线程同步 事件Event\n"<<endl;
//初始化事件和关键段 自动置位,初始无触发的匿名事件
g_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
InitializeCriticalSection(&g_csThreadCode);
HANDLE handle[THREAD_NUM];
g_nNum = 0;
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
WaitForSingleObject(g_hThreadEvent, INFINITE); //等待事件被触发
i++;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
//销毁事件和关键段
CloseHandle(g_hThreadEvent);
DeleteCriticalSection(&g_csThreadCode);
system("pause");
return 0;
}
unsigned int __stdcall Fun(PVOID pPM)
{
int nThreadNum = *(int *)pPM;
SetEvent(g_hThreadEvent); //触发事件
Sleep(50);//some work should to do
EnterCriticalSection(&g_csThreadCode);
g_nNum++;
Sleep(0);//some work should to do
cout<<"线程编号为:"<<nThreadNum<<"全局资源为:"<<g_nNum<<endl;
LeaveCriticalSection(&g_csThreadCode);
return 0;
}
运行效果如下:
可以看出来,经典线线程同步问题已经圆满的解决了——线程编号的输出没有重复,说明主线程与子线程达到了同步。全局资源的输出是递增的,说明各子线程已经互斥的访问和输出该全局资源
OK C++ 经典线程同步 事件Event(九)有几个函数(PulseEvent,ResetEvent)并未重点介绍,那是为什么呢?
PulseEvent 是一个不可靠函数,很少使用,存在的目的是向后兼容。
主要原因如下:
一个线程等待一个同步对象可以从等待状态被暂时移除了内核模式的APC,然后返回到等待状态后, APC是完整的。如果在当线程已经从等待状态中删除的时间内出现在调用PulseEvent ,该线程将不会被释放,因为PulseEvent释放只有那些等候在那一刻它被称为线程。因此,PulseEvent是不可靠的,不应该被使用的新的应用。相反,使用条件变量。
对于手动重置事件对象,可以立即释放所有等待的线程被释放。然后该函数的事件对象的状态重置为无信号和回报。
对于自动重置事件对象,该功能将释放一个等待线程后的状态为非终止和回报,即使多个线程都在等待。
如果没有线程在等待,如果没有线程可以立即释放, PulseEvent简单地设置事件对象的状态设置为无信号和回报。
请注意,对于使用多对象等待函数来等待所有指定的对象来通知线程, PulseEvent可以设置事件对象的状态为signaled和重置而不会造成等待函数返回为非终止。发生这种情况,如果不是所有指定的对象被同时发出信号。
使用SignalObjectAndWait和PulseEvent与Windows 7时要特别小心,因为使用这些API的多个线程之间可以导致应用程序死锁。是由SignalObjectAndWait信号的线程调用PulseEvent信号的SignalObjectAndWait呼叫等待的对象。在某些情况下, SignalObjectAndWait的调用者无法接收的等待对象的信号状态的时间,从而导致死锁。
上面说了那么多了,就来细细的体会这个函数吧、手动重置事件和自动重置事件的案例。
下面案例整理中待续!!!
手动重置事件(案例)
自动重置事件(案例)