现在的位置: 首页 > 综合 > 正文

白话windows多线程同步之事件对象

2019年03月10日 ⁄ 综合 ⁄ 共 2298字 ⁄ 字号 评论关闭

引子:有这样一个程序需求,做一个文本编辑器,读取文件内容后能够进行文字拼写检查,语法检查,字数统计等工作。设计要点:①必须等到读取文件内容完全之后才能进行之后的操作②为了提高程序效率,拼写检查,语法检查,字数统计等工作最好一起进行,或者根据需求每个任务单独进行。

看到上面的需要,第一眼我们的反应是用多线程可以解决,针对该程序,利用事件内核对象将比较容易的实现。下面给出代码:

#include "stdafx.h"
#include <Windows.h>
#include <process.h>

HANDLE g_hEvent = NULL ;
int s_n1 = 20 ;
int s_n2 = 20 ;
int s_n3 = 20 ;

DWORD WINAPI WordCount(LPVOID lp) ;
DWORD WINAPI SpellCheck(LPVOID lp) ;
DWORD WINAPI GrammarCheck(LPVOID lp) ;

void OpenFileAndReadContentsIntoMemory() 
{
	Sleep(3000) ;
	printf("Ready Go!!!\n") ;
}

int _tmain(int argc, _TCHAR* argv[])
{
	g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL) ;
	ResetEvent(g_hEvent) ;

	HANDLE hThrd[3] ;
	DWORD dwThreadId[3] ;
	hThrd[0] = CreateThread(NULL, 0, WordCount, NULL, 0, &dwThreadId[0]) ;
	hThrd[1] = CreateThread(NULL, 0, SpellCheck, NULL, 0, &dwThreadId[1]) ;
	hThrd[2] = CreateThread(NULL, 0, GrammarCheck, NULL, 0, &dwThreadId[2]) ;

	OpenFileAndReadContentsIntoMemory() ;

	SetEvent(g_hEvent) ;

	while(TRUE)
	{
		//必须有这段代码,否则主线程退出了整个程序就退出了,后面的过程就无法实现了
	}
	return 0;
}

DWORD WINAPI WordCount(LPVOID lp) 
{
	WaitForSingleObject(g_hEvent, INFINITE) ;
	while(s_n1-- > 0){
		Sleep(1000) ;
		printf("WordCouting %d...\n", s_n1) ;
	}
	return 0 ;
}

DWORD WINAPI SpellCheck(LPVOID lp) 
{
	WaitForSingleObject(g_hEvent, INFINITE) ;
	while(s_n2-- > 0){
		Sleep(1000) ;
		printf("SpellChecking %d...\n", s_n2) ;
	}
	return 0 ;
}

DWORD WINAPI GrammarCheck(LPVOID lp) 
{
	WaitForSingleObject(g_hEvent, INFINITE) ;
	while(s_n3-- > 0){
		Sleep(1000) ;
		printf("GrammarChecking %d...\n", s_n3) ;
	}
	return 0 ;
}

下面进行分析:

如何做到内容读取完全之后才开始后面的任务操作?

我们在程序的开始创建WordCount、SpellCheck、GrammarCheck线程之后,他们都各自跑了起来,难道不用担心

OpenFileAndReadContentsIntoMemory()函数没有执行完吗,如果该函数没有执行完,那么后面读取的内容就不完整,程序出现bug了!!!

不用担心,由于我们创建事件内核对象时,CreateEvent的第三个参数表明他的初始化状态是没有触发的,请看每个任务线程函数的第一行代码:WaitForSingleObject(g_hEvent, INFINITE) 。该函数一直会等待g_hEvent变为触发状态,否则该线程阻塞,不会往下执行。您看,

OpenFileAndReadContentsIntoMemory()函数执行完之后,调用了SetEvent(g_hEvent) ,该函数的目的就是使g_hEvent变为触发状态,

那么各个任务线程就开始工作了。是不是比较清晰呢 :-D

我们再来讨论第二个问题:由于某个用户的机器配置太烂,WordCount、SpellCheck、GrammarCheck三个任务一起执行,

我们的程序看起来比较卡,老板要你改方案。是不是要做很多复杂的操作呢?

不用!时间内核对象的好处就体现在这里了,容我慢慢到来。

有没有注意CreateEvent第二个参数,他是什么意思呢?为TRUE表明创建的是人工重置事件对象,为False则是自动重置事件对象。嘛?人工?自动?都是一些啥啊?

哈哈,自动重置事件对象就是当成功地调用WaitForSingleObject()后,g_hEvent会自动重置为非触发状态。

那么WordCount、SpellCheck、GrammarCheck中某一个线程成功地调用WaitForSingleObject()后,

g_hEvent会自动重置为非触发状态,其他的两个线程只能等待他孤单的执行了,~~~~(>_<)~~~~

慢着,这样程序就能单独的执行每个线程啦?聪明的朋友一定会说,既然g_hEvent都是非触发状态了,

后面等待的线程肯定没有机会执行,他们都饿死了。是的,的确是这样。您也一定也知道了在哪里改写代码了吧。对,就是在每个线程执行完之后再调用SetEvent。

抱歉!评论已关闭.