12.队列的应用-事件驱动编程

1.事件驱动编程

前面我们提到队列在操作系统中用的非常多,其中一大应用就是基于事件驱动的消息队列机制,熟悉Windows SDK程序设计的对于Windows的消息产生、分发、处理应该不会陌生。

事件驱动编程简而言之就是应用程序维护一个或多个事件队列,完全以事件队列为主线来设计和编写整个程序。如Windows就是以消息队列为中心来对每个消息进行处理从而实现事件驱动编程(如鼠标、键盘等事件的处理)。

2.离散事件模拟

现在为了演示事件驱动编程,我可能并没有能力去构建一个windows消息系统,这里以类似的应用:[银行排队模拟]来演示整个程序。

银行排队模拟描述如下:

假设一个银行有4个窗口对外接待客户。由于每个窗口在某一时刻只能接待一个客户,在客户众多的时候需要排队,对于刚进入银行的客户,如果某个窗口正空闲,则可上前办理业务,如果所有窗口都不空闲则排在人数最少的窗口。现在要求模拟银行的某一时间段内的4个窗口的客户排队情况。这里客户到达的时刻和办理业务的时间都是随机的。

可以看到不同的时刻4个窗口的客户排队情况是不一样的,这个客户排队情况完全是以时间推移来驱动的,那么我们就考虑建立一个基于时间的事件队列,按照事件发生的先后排列事件。这种类似的应用统称为离散事件模拟。

3.实现方式

事件队列元素定义如下

typedef struct
{
	int		nOccurTime;	//事件发生时间
	int		nType;		//事件类型,0-客户到达事件,1、2、3、4-对应窗口客户离开事件
}JWArrayElem_event;

窗口队列元素定义如下

typedef struct
{
	int nArriveTime;		//客户到达事件
	int nDurationTime;		//客户办理业务所需时间
}JWArrayElem_window;

为了实现上述离散事件模拟,这里我们建立一个事件队列,4个窗口排队队列。

初始化上述队列,在事件队列中插入第一个客户到达事件以启动事件驱动。

开启计时,检测是否有事件要发生。

如果是客户到达事件,则设置客户办理业务时间,插入到窗口队列中,同时要插入下一个客户到达事件。如果是当前窗口的第一个客户,还要插入一个客户离开事件以驱动当前窗口的客户离开事件。

如果是客户离开事件,则从对应窗口队列中取出客户并设置下一个客户离开事件,在同一窗口下前一个客户离开事件没有发生之前是不可能发生第二个客户离开事件的。

4.实际程序

#include <stdio.h>
#include <process.h>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>

#include "JWArray.h"

JWArray(event)		*pEventQueue;					//事件队列
JWArrayElem(event)	eventElem;						//事件元素
JWArray(window)		*pWindowsQueue[5];				//窗口队列,为了使用方便,下标从1开始
JWArrayElem(window)	windowElem;						//窗口元素
int					nCloseTime = 100;				//银行关闭时间

JWArray_BOOL EventCompare(JWArrayElem(event) elem1, JWArrayElem(event) elem2);
void ShowState(int nCurTime);
void Init();
void Clear();
int  CalcMinWindow();
void CustomerArrived(int nArriveTime);
void CustomerLeave(int nCurTime, int iLeave);
void EventPump();
void ShowState(int nCurTime);

int main(int argc, char *argv[])
{
	//开启事件泵
	EventPump();

	return 0;
}

/**
 *功能:	事件队列元素比较函数
 *参数:	elem1,elem2 -- 两个事件队列元素
 *返回:	elem1的发生时间小于elem2则返回JWArray_TRUE,否则返回JWArray_FALSE
 *其他:	2014/05/05 By Jim Wen Ver1.0
**/
JWArray_BOOL EventCompare(JWArrayElem(event) elem1, JWArrayElem(event) elem2)
{
	return elem1.nOccurTime  < elem2.nOccurTime ? JWARRAY_TRUE : JWARRAY_FALSE;
}

/**
 *功能:	初始化--分配事件队列和窗口队列,在事件队列插入启动的客户到达事件
 *参数:	无
 *返回:	无
 *其他:	2014/05/05 By Jim Wen Ver1.0
**/
void Init()
{
	int i;

	//分配队列
	pEventQueue = JWArrayCreate(event)(20, 10);
	for (i = 1; i < 5; i++)
	{
		pWindowsQueue[i] = JWArrayCreate(window)(20, 10);
	}

	//在事件队列插入第一个客户到达事件
	eventElem.nOccurTime	= 0;
	eventElem.nType			= 0;

	JWArrayEnQueue(event)(pEventQueue, eventElem);
}

/**
 *功能:	清理工作--清理事件队列和窗口队列
 *参数:	无
 *返回:	无
 *其他:	2014/05/05 By Jim Wen Ver1.0
**/
void Clear()
{
	int i;

	//清理队列
	JWArrayDestroy(event)(pEventQueue);
	for (i = 1; i < 5; i++)
	{
		JWArrayDestroy(window)(pWindowsQueue[i]);
	}
}

/**
 *功能:	计算当前窗口队列前人数最少的窗口号
 *参数:	无
 *返回:	人数最少的窗口队列号
 *其他:	2014/05/05 By Jim Wen Ver1.0
**/
int CalcMinWindow()
{
	int nLength ;
	int i ;
	int nIndex ;

	nIndex	 = 1;
	nLength  = JWArrayGetLength(window)(pWindowsQueue[1]);
	for (i = 2; i <=4 ; i++)
	{
		if (JWArrayGetLength(window)(pWindowsQueue[i]) < nLength)
		{
			nIndex = i;
			nLength  = JWArrayGetLength(window)(pWindowsQueue[i]);
		}
	}

	return nIndex;
}

/**
 *功能:	处理客户到达事件
 *参数:	nArriveTime--客户到达的时间
 *返回:	无
 *其他:	2014/05/05 By Jim Wen Ver1.0
**/
void CustomerArrived(int nArriveTime)
{
	int i;
	int nDurationtime;			//当前客户办理业务消耗时间
	int nNextInterTime;			//距离下一个客户到达时间

	//随机生成nDurationtime和nNextInterTime
	srand( (unsigned)time( NULL ) );
	nDurationtime = rand() % 30 +1;
	nNextInterTime = rand() % 5 +1;

	//设置下一个客户到达事件
	if (nArriveTime + nNextInterTime < nCloseTime)
	{
		eventElem.nOccurTime = nArriveTime + nNextInterTime;
		eventElem.nType = 0;

		JWArrayOrderInsert(event)(pEventQueue, eventElem, EventCompare);
	}

	//设置当前窗口排列队形
	i = CalcMinWindow();
	windowElem.nArriveTime = nArriveTime;
	windowElem.nDurationTime = nDurationtime;
	JWArrayEnQueue(window)(pWindowsQueue[i], windowElem);

	//如果是第一个到达客户,插入一个离开事件,否则此后的离开事件在前一个离开事件发生后插入
	if (JWArrayGetLength(window)(pWindowsQueue[i]) == 1)
	{
		eventElem.nOccurTime = nArriveTime + nDurationtime;
		eventElem.nType = i;

		JWArrayOrderInsert(event)(pEventQueue, eventElem, EventCompare);
	}
}

/**
 *功能:	处理客户离开事件
 *参数:	nCurTime--客户离开事件发生时间,iLeave--客户离开事件发生的窗口队列号
 *返回:	无
 *其他:	2014/05/05 By Jim Wen Ver1.0
**/
void CustomerLeave(int nCurTime, int iLeave)
{
	//window队列出列
	JWArrayDeQueue(window)(pWindowsQueue[iLeave], NULL);

	//设置下一个客户离开时间
	if (JWARRAY_FALSE == JWArrayIsEmpty(window)(pWindowsQueue[iLeave]))
	{
		JWArrayGetHead(window)(pWindowsQueue[iLeave], &windowElem);

		eventElem.nOccurTime = nCurTime + windowElem.nDurationTime;//注意这里实际离开时间是当前离开事件发生的时间加上下一个客户要办理业务的时间,不能写成下一个客户到达的时间加上下一个客户要办理业务的时间,这是不对的,因为客户到达时不能立即开始办理,要等待!!!
		eventElem.nType = iLeave;

		JWArrayOrderInsert(event)(pEventQueue, eventElem, EventCompare);
	}
}

/**
 *功能:	开启消息泵,依据时间对事件队列处理从而推动整个事件的发生
 *参数:	无
 *返回:	无
 *其他:	2014/05/05 By Jim Wen Ver1.0
**/
void EventPump()
{
	int i=0;

	Init();

	do
	{
		if (JWARRAY_FALSE != JWArrayGetHead(event)(pEventQueue, &eventElem))
		{
			while (eventElem.nOccurTime == i)//处理当前时刻可能同时存在的多个事件
			{
				JWArrayDeQueue(event)(pEventQueue, &eventElem);

				if (eventElem.nType == 0)
				{
					CustomerArrived(eventElem.nOccurTime);
				}
				else
				{
					CustomerLeave(i, eventElem.nType);
				}

				if (JWARRAY_FALSE == JWArrayGetHead(event)(pEventQueue, &eventElem))
				{
					break;
				}
			}
		}
		else
		{
			break;
		}

		ShowState(i++);
		Sleep(100);
	} while (1);

	Clear();
}

/**
 *功能:	显示当前事件队列和各个窗口队列的情况
 *参数:	无
 *返回:	无
 *其他:	2014/05/05 By Jim Wen Ver1.0
**/
void ShowState(int nCurTime)
{
	system("cls");
	printf("当前时间:%d\n\n", nCurTime);

	printf("--------------------------------------------------------------------------------\n");
	printf("事件队列\t");
	JWArrayTraverse(event)(pEventQueue, JWArrayPrintfElem(event));
	printf("\n--------------------------------------------------------------------------------\n\n");

	printf("\n--------------------------------------------------------------------------------\n");
	printf("窗口1\t");
	JWArrayTraverse(window)(pWindowsQueue[1], JWArrayPrintfElem(window));
	printf("\n--------------------------------------------------------------------------------\n");
	printf("窗口2\t");
	JWArrayTraverse(window)(pWindowsQueue[2], JWArrayPrintfElem(window));
	printf("\n--------------------------------------------------------------------------------\n");
	printf("窗口3\t");
	JWArrayTraverse(window)(pWindowsQueue[3], JWArrayPrintfElem(window));
	printf("\n--------------------------------------------------------------------------------\n");
	printf("窗口4\t");
	JWArrayTraverse(window)(pWindowsQueue[4], JWArrayPrintfElem(window));
	printf("\n--------------------------------------------------------------------------------\n");
}

程序说明:

1.事件时间单位为秒

2.假设客户办理业务不超过30秒,相邻两个客户到达时间间隔不超过5秒

3.银行在100秒后不再允许客户进入

4.不考虑客户在排队过程中换队的情况

5.这里对于event队列构建了一个顺序插入函数JWArrayOrderInsert(event)

程序运行效果如下

完整事件驱动编程源代码下载链接

原创,转载请注明来自http://blog.csdn.net/wenzhou1219

12.队列的应用-事件驱动编程

时间: 2024-10-10 22:13:09

12.队列的应用-事件驱动编程的相关文章

Linux的I/O模式、事件驱动编程模型

大纲: (1)基础概念回顾 (2)Linux的I/O模式 (3)事件驱动编程模型 (4)select/poll/epoll的区别和Python示例 网络编程里常听到阻塞IO.非阻塞IO.同步IO.异步IO等概念,总听别人装13不如自己下来钻研一下.不过,搞清楚这些概念之前,还得先回顾一些基础的概念. 1.基础知识回顾 注意:咱们下面说的都是Linux环境下,跟Windows不一样哈~~~ 1.1 用户空间和内核空间 现在操作系统都采用虚拟寻址,处理器先产生一个虚拟地址,通过地址翻译成物理地址(内

事件驱动编程

看完公司的基于Netty的游戏框架,框架中用到了多态,函数式编程和事件驱动编程,第一次看到事件驱动的时候,就想到跟观察者模式很像. 事件驱动初上手感觉还很好用,在我自己写的项目里,要写很多爬虫,比如下面爬虫的例子,我只是想关心拼接URL地址,和关心不同的网站怎么解析DOM元素,写一个回调就好 多态,函数式编程和事件驱动编程,这三个还是然让我学到很多,可以用一个框架的基础,比如在Netty中,继承SimpleChannelInboundHandler<TextWebSocketFrame>,实现

关于Web开发里并发、同步、异步以及事件驱动编程的相关技术

一.开篇语 我的上篇文章<关于如何提供Web服务端并发效率的异步编程技术>又成为了博客园里“编辑推荐”的文章,这是对我写博客很大的鼓励,也许是被推荐的原因很多童鞋在这篇文章里发表了评论,有童鞋说我这篇文章理论化很严重,没有实际代码和具体项目做支撑,这个评论让我有种理论和实践脱节的味道,所以我想在这里谈谈我为什么要写这篇文章的原因,这篇文章是把我前不久学习多线程编程的一个总结. 当我从我书堆里找到所有与多线程开发相关的书籍简单阅读后,我发现了一个问题,在java里开发多线程最强有力的实践就是做服

理解Node.js事件驱动编程

Node.js现在非常活跃,相关生态社区已经超过Lua(基本上比较知名的功能都有nodejs模块实现). 但是我们为何要使用Node.Js?相比传统的webserver服务模式,nodejs有什么优点优势? Node.Js是基于javascript语言,建构在google V8 engine以及Linux上的一个非阻塞事件驱动IO框架.nodejs是单进程单线程,但是基于V8的强大驱动力,以及事件驱动模型,nodejs的 性能非常高,而且想达到多核或者多进程也不是很难(现在已经有大量的第三方mo

图形用户界面编程——事件驱动编程

根据事件发生而执行代码的编程方式,称为事件驱动编程 事件源——>监听——>处理事件(处理函数) ps:实现按键全选和反选 运行结果图

js 中的事件驱动编程

js 中的事件驱动编程,布布扣,bubuko.com

dojo事件驱动编程之事件绑定

什么是事件驱动? 事件驱动编程是以事件为第一驱动的编程模型,模块被动等待通知(notification),行为取决于外来的突发事件,是事件驱动的,符合事件驱动式编程(Event-Driven Programming,简称EDP)的模式. 何谓事件?通俗地说,它是已经发生的某种令人关注的事情.在软件中,它一般表现为一个程序的某些信息状态上的变化.基于事件驱动的系统一般提供两类的内建事件(built-in event):一类是底层事件(low-level event)或称原生事件(native ev

Android事件驱动编程-基于EventBus(二)

Android事件驱动编程(二) --欢迎转载,请注明出处 http://blog.csdn.net/asce1885 ,未经本人同意请勿用于商业用途,谢谢-- 原文链接:https://medium.com/google-developer-experts/event-driven-programming-for-android-part-ii-b1e05698e440 本文Gitbooks链接:http://asce1885.gitbooks.io/android-rd-senior-adv

Android事件驱动编程-基于EventBus(一)

Android事件驱动编程-基于EventBus(一) --欢迎转载,请注明出处 http://blog.csdn.net/asce1885 ,未经本人同意请勿用于商业用途,谢谢-- 原文链接:https://medium.com/google-developer-experts/event-driven-programming-for-android-part-i-f5ea4a3c4eab 本文Gitbooks链接:http://asce1885.gitbooks.io/android-rd-