• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C++事件驱动型银行排队模拟

C++事件驱动型银行排队模拟

作者:xmwd 字体:[增加 减小] 来源:互联网 时间:2017-05-28

xmwd 通过本文主要向大家介绍了c++事件,c++鼠标点击事件,c++按钮事件,c++键盘事件,c++驱动开发等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

最近重拾之前半途而废的C++,恰好看到了《C++ 实现银行排队服务模拟》,但是没有实验楼的会员,看不到具体的实现,正好用来作为练习。 

模拟的是银行的排队叫号系统,所有顾客以先来后到的顺序在同一个队列中等待,当有服务窗口空闲时,则队首的顾客接受服务,完成后则下一位顾客开始接受服务。 

本实现是事件驱动型的,处理对象是事件而不是顾客:
 有2种事件:顾客到事件和顾客离开事件。
 有2个队列:顾客队列和事件队列。

 程序的逻辑如下:
 1.初始化事件队列,填充顾客到达事件;
 2.处理事件队列的头部(总是为最早发生的事件),若为“顾客到达事件”,转向处理“顾客到达事件”,若为“顾客离开事件”,转向处理“顾客离开事件”;
 3.循环进行2,直到事件队列为空或超出营业时间;
 4.进行清理工作。 

事件处理 

主流程 

永远处理事件队列的头部——即最早发生的事件。

 void Manager::run() {
 while (_event_queue.size() != 0) {
  _current_event = _event_queue.front();
  if (_current_event->occur_time >= _total_serve_time)//超出营业时间
   break;
  if(_customer_queue.size() == 0 && _event_queue.size() <= _service_num)//生成新的顾客到达事件
  {
   generate_arrived_event(_generate_arrived_time);
   _current_event = _event_queue.front();//update current event, deal it with order
  }

  if (_current_event->event_type == EventType::ARRIVIED)//处理顾客到达事件
   customer_arrived();
  else if (_current_event->event_type == EventType::DEPARTURE)//处理顾客离开事件
   customer_departure();
 }
} 
</div>

生成顾客到达事件 

有2种方法:
 (1)一次性生成全部顾客到达事件
 设定单位时间内到达的顾客数量,生成银行营业时间内所有的到达事件,处理这些事件,直到事件队列为空或是超过了银行的营业时间。

 void Manager::generate_arrived_event(int& current_time) {//生成单位时间内的顾客到达事件并入队,current_time是共享的
 Event* event;
 int customer_per_minute = Random::uniform(RANDOM_PER_MINUTE);//单位时间内随机到达的顾客数量,至少为1例
 while (customer_per_minute > 0) {
  event = new Event(current_time);
  _event_queue.enqueue(event);
  --customer_per_minute;
 }
 ++current_time;
 }
 

 _generate_arrived_time = 0;
 while(_generate_arrived_time < _total_serve_time) { //_total_serve_time是银行的总营业时间
  generate_arrived_event(_generate_arrived_time);
 } 

</div>

(2)分批生成顾客到达事件
 仅在需要时生成顾客到达事件,因为顾客接受服务需要一定的时间,通常来说第1种方法生成的到达事件在达到银行营业时间后不能全部处理完成,这就造成了时间和空间上的浪费。分批生成到达事件的实现是基于这样的事实:如果顾客队列为空且事件队列的长度小于等于服务窗口的数量时,说明余下的事件将不能让服务窗口满载并产生等待服务的顾客,此时就需要生成新的顾客到达事件。
 初始化事件队列:

 _generate_arrived_time = 0;//依旧是以时间顺序生成顾客到达事件
 while(_generate_arrived_time < INIT_ARRIVIED_EVENT_NUM) {//选择合适的值,使最初生成的顾客到达事件略多于服务窗口数量,保证服务窗口满载且有等待顾客即可
  generate_arrived_event(_generate_arrived_time);
 } 
</div>

判断条件并生成到达事件:

 if(_customer_queue.empty() && _event_queue.size() <= _service_num)
{
 generate_arrived_event(_generate_arrived_time);
 _current_event = &_event_queue.top();//update current event, deal it with order
} 
</div>

由于顾客到达事件仍是以时间顺序从0到营业时间结束来生成的,之所以能够保证不会在处理了98分钟时的顾客离开事件(可能是5分钟时到达的顾客产生的)后再去处理新插入的10分钟时的顾客到达事件,就是初始化事件队列时选择合适的INIT_ARRIVIED_EVENT_NUM的意义所在:时刻保证事件队列的长度大于服务窗口的数量,新生成的顾客到达事件的时间若早于顾客离开事件的时间(以时间为顺序生成顾客到达事件就保证了新生成的顾客到达事件的时间不小于事件队列中已有的顾客到达事件,而顾客离开事件的时间是随机的,若提前于新生成的顾客到事件处理,可能会失序)也能被正确处理。 

生成顾客离开事件 

顾客队列头部顾客接受服务并出队,生成顾客离开事件并入队事件队列。顾客离开事件的发生时间 = 当前时间 + 顾客接受服务的时长。

 void Manager::generate_departure_event(int service_index, int current_time) {
 _services[service_index].serve_customer(*_customer_queue.front());
 _services[service_index].set_busy();//服务窗口置为“忙”
 _services[service_index].set_service_start_time(current_time);//服务开始时间
 _customer_queue.dequeue();

 int duration = _services[service_index].get_customer_duration();
 Event* event = new Event(current_time + duration, EventType::DEPARTURE, service_index);//生成顾客离开事件
 _event_queue.enqueue(event);
} 

</div>

处理顾客到达事件

处理“顾客到达事件”的逻辑:
 1.生成1个顾客,入队顾客队列;
 2.出队事件队列;
 3.若有空闲的服务窗口,则生成“顾客离开事件”。 

下面是代码:

void Manager::customer_arrived() {
 int idle_service_num = get_idle_service_index();//获取空闲的服务窗口,返回-1说明未找到
 int current_time = _current_event->occur_time;
 Customer* customer = new Customer(current_time);//顾客到达事件发生时间即为顾客到达时间, 顾客接受服务的时长随机
 _customer_queue.enqueue(customer);
 _event_queue.dequeue();
  
 if (idle_service_num != -1)
  generate_departure_event(idle_service_num, current_time);
} 
</div>

处理顾客离开事件 

处理“顾客离开事件”的逻辑:
 1.顾客所在服务窗口置为空闲,统计顾客信息;
 2.出队事件队列;
 3.若顾客队列不为空且有空闲的服务窗口,生成“顾客离开事件”。 

下面是代码:

 void Manager::customer_departure() {
 int current_time = _current_event->occur_time;
 int service_index = _current_event->service_index;//顾客离开的服务窗口

 _customer_stay_time += current_time -
     _services[service_index].get_customer_arrive_time();//统计顾客在银行的滞留时间
 ++_total_served_customer_num;//接受服务的顾客数目加1
 _services[service_index].set_idle();
 _event_queue.dequeue();

 if(_customer_queue.size() > 0) {
  service_index = get_idle_service_index();//有顾客离开,必然可以获得1个空闲服务窗口,这里获取最小序号的服务窗口
  generate_departure_event(service_index, current_time);
 }
} 

</div>

清理工作:
 1.寻找仍在接受服务的顾客并统计他们的信息;
 2.释放动态申请的内存。 

下面是代码:

 void Manager::end() {
 for (int i = 0; i < _service_num; i++) {
  if (!_services[i].is_idle()) {//统计正在接受服务的顾客的信息
   int service_start_time = _services[i].get_service_start_time();
   int arrive_time = _services[i].get_customer_arrive_time();
   int duration = _services[i].get_customer_duration();

   _customer_stay_time += service_start_time + duration - arrive_time;
   ++_total_served_customer_num;
  }
 }

 //释放动态申请的内存
 _customer_queue.clear();
 _event_queue.clear();
 delete[] _services;
} 

</div>

关于队列的说明 

程序中使用的是自定义的队列,根据需求,可以使用STL中的优先队列和队列,前者用于事件队列,后者用于顾客队列。
 优先队列的头部总是优先级最高的节点,对于事件来说,就是发生的时间越早,事件优先级越高,所以这是一个最小堆——时间发生的时间最小(最早)的位于堆顶。这涉及到对Event类型的比较,使用STL的greater<Event>(需要重载operator>)或是自定义

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C++事件驱动型银行排队模拟
  • C++事件处理中__event与__raise关键字的用法讲解
  • C++事件处理中的__hook与__unhook用法详解

相关文章

  • 2017-05-28c语言生成随机数的方法(获得一组不同的随机数)
  • 2017-08-30Codeforces 842B. Gleb And Pizza 模拟
  • 2017-05-28C++实现获取IP、子网掩码、网关、DNS等本机网络参数的方法
  • 2017-05-28VC WinExec打开指定程序或者文件的方法
  • 2017-05-28简单了解C++语言中的二元运算符和赋值运算符
  • 2017-05-28strcat函数与strncat函数的深入分析
  • 2017-12-31素数筛选法
  • 2017-05-28C++中汉字字符串的截取
  • 2017-05-28编写C++程序使DirectShow进行视频捕捉
  • 2017-05-28使用C语言中的time函数获取系统时间

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C++大数模板(推荐)
    • C语言游戏必备:光标定位与颜色设置的实现方法
    • C++中图片重命名实现代码
    • GCC 编译使用动态链接库和静态链接库的方法
    • C++类和对象实例解析(二)
    • C++ 11实现检查是否存在特定的成员函数
    • 深入理解C语言内存对齐
    • C语言实现稀疏矩阵
    • c/c++ 奇技淫巧(一些c语言的技巧)
    • C++的虚析构详解及实例代码

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有