專案實戰筆記 | C++ 事件驅動模型實現銀行排隊服務2 程式碼實現
Oop程式設計思想,概率程式設計,驅動的設計,蒙特卡洛方法,CPU資源爭奪模型
我們這裡使用連結串列實現佇列,這裡實現佇列的類,使用帶頭結點的連結串列實現
這個專案我在最後犯了一個很大的錯誤,我的程式總是編譯不通過,提示expect class-name before ‘class’,查閱了大量資料還是沒有找到解決方法,後來發現是#endif 的後面有一個空行,空行裡面有幾個空格鍵,所以以後在程式設計的時候當編譯器提示你expect class-name before ‘class’,或者是expect ; before{,檢查一下#ifndef #define #endif是否有問題,#endif
1)通過這個專案的學習,我學會了幾個重要的思想,首先就是對於標頭檔案而言,最好加上保險頭,也就是防止標頭檔案重複宣告的一個數據結構,其宣告方法為
#ifndef A
#define A
#endif
有了這樣的資料結構之後,就算標頭檔案被重複引用,也不會發生重複宣告的錯誤
2)然後就是模板類的使用,在自己定義的queue中最好使用temp<typenameT>來定義型別,這樣就能夠通過queue<T>的呼叫方法來建立指定型別的佇列
3)有關c++語言隨機數的使用方法,比如,srand生成0~RAND_MAX大小的隨機數,它可以通過
4)還有就是對於事件驅動的程式的設計方法是如何使用的
對於函式而言,可以使用預設行參,比如
Int a(int b=1,int c=2)
那麼呼叫a可以使用
a(),a(1),a(1,1)這三種方法,依次不傳入引數,傳入一個引數,傳入兩個引數
5)exit(-1)表示非正常退出,對於作業系統而言,exit(0)是正常退出,而-1是非正常退出,通過返回值的不同我們就能得知是否是正常退出
6)這個專案為了取得平均顧客停留時間和平均單位時間顧客數,使用了十萬次模擬的資料總和取平均數,這樣的思想能夠是的結果更精確,以後在做專案的時候記得參考
7)這個專案的思路就是,這個專案有三個佇列,一個事件佇列,一個顧客佇列,一個視窗佇列,首先初始化一個到達事件,呼叫到達函式,對於每一個到達事件而言,隨機生成下一個到達事件,事件列表裡面任何時候最多都只有一個到達事件,到達函式的功能是,當發現有空閒視窗的時候,將顧客送入空閒視窗,然後生成一個離開事件否則將顧客送入等待視窗,
當處理離開事件的時候,呼叫離開函式,離開函式的功能是當顧客等待佇列裡面有顧客在等待的時候,將等待的顧客送入空閒視窗,並且生成一個離開函式,當等待佇列裡面沒有顧客在等待的時候,將當前視窗設定為空閒狀態。
Queue.hpp:
//對於自己寫的佇列類或者是其他而言,佇列元素最好是模板類,類似於T
/*****queue.hpp*****/
//
// Queue.hpp
// QueueSystem
//
// 構造一個帶頭結點的鏈式佇列,節點位於佇列尾部
Queue() {
this->front = new T;
// 如果記憶體申請失敗,則不應該繼續執行程式了
if (!this->front) {
exit(-1);//判斷某個指標是否申請成功,或者是否為空,可以使用if語句直接判斷,exit(-1),對於作業系統而言,0為正常退出,非0為非正常退出
}
// 初始化節點
this->front->next = NULL;
this->rear = this->front;
}
// 銷燬一個佇列時候需要釋放節點中申請的記憶體
~Queue() {
// 清空當前佇列中的元素
this->clearQueue();
// 再刪除頭結點
delete this->front;
}
T* enqueue(T&node) {
T *new_node = new T;
if (!new_node) {
exit(-1);
}
*new_node = node;
this->rear->next = new_node;
this->rear = new_node;
return this->front;
}
// 出隊時,從隊頭元素出隊
T* dequeue() {
// 如果佇列空,則返回空指標
if (!this->front->next) {
return NULL;
}
T *temp_node;
temp_node = this->front->next;
this->front->next =temp_node->next;
// 如果佇列中只有一個節點,那麼記得將隊尾節點指標置為頭結點
if (this->rear == temp_node) {
this->rear = this->front;
}
return temp_node;
}
T*orderEnqueue(Event &event) {
Event* temp = new Event;
if (!temp) {
exit(-1);
}
*temp = event;
// 如果這個列表裡沒有事件, 則把 temp 事件插入
if (!this->front->next) {
this->enqueue(*temp);
return this->front;
}
// 按時間順序插入
Event *temp_event_list = this->front;
// 如果有下一個事件,且下一個事件的發生時間小於要插入的時間的時間,則繼續將指標後移
while (temp_event_list->next &&temp_event_list->next->occur_time < event.occur_time) {
temp_event_list =temp_event_list->next;
}
// 將事件插入到佇列中
temp->next = temp_event_list->next;
temp_event_list->next = temp;
// 返回佇列頭指標
return this->front;
}
int length() {
T *temp_node;
temp_node = this->front->next;
int length = 0;
while (temp_node) {
temp_node = temp_node->next;
++length;
}
return length;
}
voidclearQueue() {
T *temp_node;
while (this->front->next) {
temp_node = this->front->next;
this->front->next =temp_node->next;
delete temp_node;
}
this->front->next = NULL;//不知為何這裡要再次設為一個NULL,所以以後在銷燬某個指標指向的指標的時候記得把前一個指標設為NULL
this->rear = this->front;
}
//注意,清空佇列的邏輯應該注意,我們在依次刪除完佇列中元素的記憶體時,應該將頭和尾節點進行復位,否則會出現很嚴重的記憶體洩露問題,因為我們的入隊是通過尾指標實現的。
//本專案對於記憶體使用這樣一個理念,誰申請,誰釋放
接下來是QueueSystem的設計:
//
// QueueSystem.cpp
// QueueSystem
//
/*****QueueSystem.cpp*****/
QueueSystem::QueueSystem(inttotal_service_time, int window_num):
total_service_time(total_service_time),
window_num(window_num),
total_customer_stay_time(0),
total_customer_num(0) {
// 建立服務視窗
this->windows = new ServiceWindow[window_num];
}
QueueSystem::~QueueSystem() {
delete[] windows;
}
voidQueueSystem::simulate(int simulate_num) {
double sum = 0;
for (int i = 0; i != simulate_num; ++i) {
// 每一遍執行,我們都要增加在這一次模擬中,顧客逗留了多久
//在這裡sum得到的是這100000次模擬顧客平均停留時間,這裡加了100000次
sum += run();
}
// 計算平均逗留時間
avg_stay_time = (double)sum / simulate_num;
//這裡的double也是沒有必要的,不過這樣更規範
// total_customer_num記錄的是這100000次模擬中的顧客到來的總數
avg_customers = (double)total_customer_num/ (total_service_time*simulate_num);
}
voidQueueSystem::init() {
// 第一個事件肯定是到達事件, 使用預設構造
Event *event = new Event;
current_event = event;
}
// 系統開始執行,不斷消耗事件表,當消耗完成時結束執行
doubleQueueSystem::run() {
this->init();
while (current_event) {
// 判斷當前事件型別
if (current_event->event_type == -1){
customerArrived();
} else {
customerDeparture();
}
delete current_event;
//我覺得當對一個指標重新賦值的時候,直接給這個指標賦值就好了,不需要delete,要不然每一處指標重新賦值都需要delete那麼不是非常的麻煩麼
// 獲得新的事件
current_event = event_list.dequeue();
};
this->end();
// 返回顧客的平均逗留時間
return(double)total_customer_stay_time/total_customer_num;
}
// 系統執行結束,將所有服務視窗置空閒,並清空使用者的等待佇列和事件列表
voidQueueSystem::end() {
// 設定所有視窗空閒
for (int i=0; i != window_num; ++i) {
windows[i].setIdle();
}
// 顧客佇列清空
customer_list.clearQueue();
// 這個時候事件列表已經為空了,寫這一句可能是為了保險起見吧,可能是顯得更規範吧,不過我覺得這一句是沒有什麼作用的,清空事件列表
event_list.clearQueue();
}
// 處理使用者到達事件
voidQueueSystem::customerArrived() {
total_customer_num++;
// 生成下一個顧客的到達事件
int intertime = Random::uniform(100); // 生成一個[0,,100)的隨機數,下一個顧客到達的時間間隔,我們假設100分鐘內一定會出現一個顧客
int time = current_event->occur_time +intertime;
Event temp_event(time);
// 如果下一個顧客的到達時間小於服務的總時間,就把這個事件插入到事件列表中
// 同時將這個顧客加入到 customer_list 進行排隊
if (time < total_service_time) {
event_list.orderEnqueue(temp_event);
} // 否則不列入事件表,且不加入 cusomer_list
// 處理當前事件中到達的顧客
Customer *customer = newCustomer(current_event->occur_time);
if (!customer) {
exit(-1);
}
customer_list.enqueue(*customer);
// 如果當前視窗有空閒視窗,那麼直接將這個顧客送入服務視窗
int idleIndex = getIdleServiceWindow();
if (idleIndex >= 0) {
customer = customer_list.dequeue();
windows[idleIndex].serveCustomer(*customer);
windows[idleIndex].setBusy();
// 顧客到視窗開始服務時,就需要插入這個顧客的一個離開事件到 event_list 中
// 離開事件的發生時間 = 當前時間事件的發生時間 + 服務時間
Eventtemp_event(current_event->occur_time + customer->duration, idleIndex);
event_list.orderEnqueue(temp_event);
}
delete customer;
//這裡還需要注意的一個細節就是當建立一個類或結構體的物件的時候,在程式的最後不需要delete它,比如Event temp_event,但當動態建立一個指標的時候,在程式的最後需要釋放它,這裡就是這樣的
}
voidQueueSystem::customerDeparture() {
if (current_event->occur_time <total_service_time) {
total_customer_stay_time += current_event->occur_time- windows[current_event->event_type].getCustomerArriveTime();
if (customer_list.length()) {
Customer *customer;
customer = customer_list.dequeue();
windows[current_event->event_type].serveCustomer(*customer);
//當一個處理離開事件的時候,需要立刻服務下一個顧客,如果顧客的離開時間大於銀行的營業時間,那什麼事情都不會做,會不斷的消耗事件佇列裡面的時間類,當事件類消耗完畢的時候,也就是程式結束的時候
Event temp_event(
current_event->occur_time +customer->duration,
current_event->event_type
);
event_list.orderEnqueue(temp_event);
delete customer;
} else {
// 如果顧客等待佇列沒有人,那麼這個銀行視窗會被設為空閒狀態,等待下一個顧客的到來,這就是事件驅動模型的執行原理
windows[current_event->event_type].setIdle();
}
}
}
來源: 實驗樓
連結: https://www.shiyanlou.com/courses/557
相關推薦
專案實戰筆記 | C++ 事件驅動模型實現銀行排隊服務2 程式碼實現
Oop程式設計思想,概率程式設計,驅動的設計,蒙特卡洛方法,CPU資源爭奪模型 我們這裡使用連結串列實現佇列,這裡實現佇列的類,使用帶頭結點的連結串列實現 這個專案我在最後犯了一個很大的錯誤,我的程式總是編譯不通過,提示expect class-name before ‘
事件驅動模型的簡單Java實現
事件驅動模型的原理不再贅述,Swing是不錯的實現。別人也有不錯的博文來說明原理。 本文的目的是提供一種簡單的,可供參考的簡短程式碼,用來幫助理解該模型。 Project Navigator Event 事件通用介面:
使用事件驅動模型實現高效穩定的網路伺服器程式
前言 事件驅動為廣大的程式設計師所熟悉,其最為人津津樂道的是在圖形化介面程式設計中的應用;事實上,在網路程式設計中事件驅動也被廣泛使用,並大規模部署在高連線數高吞吐量的伺服器程式中,如 http 伺服器程式、ftp 伺服器程式等。相比於傳統的網路程式設計方式,事件驅
Guava ---- EventBus事件驅動模型
sim div spa tar 共享 execution ext 實例 處理 在軟件開發過程中, 難免有信息的共享或者對象間的協作。 怎樣讓對象間信息共享高效, 而且耦合性低。 這是一個難題。 而耦合性高將帶來編碼改動牽一發而動全身的連鎖效應。 Spring的風靡正
事件驅動模型
image 獨立 之間 如果 png 實例 相等 不知道 響應時間 一、為什麽要用事件驅動模型? 在UI編程中,常常要對鼠標點擊進行相應,首先如何獲得鼠標點擊呢?方式一:創建一個線程,該線程一直循環檢測是否有鼠標點擊。 那麽這個方式有以下幾個缺點:1. CPU資源浪費,可能
SpringBoot整合RabbitMQ之Spring事件驅動模型
實戰背景:在進入RabbitMQ各大技術知識點之前,我們先來談談跟事件驅動息息相關的ApplicationEvent、ApplicationListener以及ApplicationEventPublisher這三大元件,點選進去看其原始碼可以發現裡面使用的CachingConnectionFa
Spark Streaming實時流處理專案實戰筆記
第二章 分散式日誌收集框架Flume 課程目錄 業務現狀分析=>flume概述=>flume架構及核心元件=>flume環境部署=>flume實戰 1、業務現狀分析 WebServer/ApplicationServer分散在各個機器上 大資
Spark Streaming實時流處理專案實戰筆記一
Spark Streaming實時流處理專案實戰筆記一 視訊資源下載:https://download.csdn.net/download/mys_mys/10778011 第一章:課程介紹 Hadoop環境:虛擬機器Centos6.4 Window:VMware 本地登入到
一、NodeJS事件驅動模型
接觸nodejs有兩個月,對nodejs的兩大特性一直有點模糊,即非同步IO和事件驅動。通過對《深入淺出nodejs》和幾篇部落格的閱讀以後,有了大致的瞭解,總結一下。 幾個例子 在開始之前,先來看幾個簡單例子,這也是我在使用nodejs時候遇到的幾個比較困惑的例子。 example
nodejs的事件驅動模型
Node.Js是基於javascript語言,建構在google V8 engine以及Linux上的一個非阻塞事件驅動IO框架。nodejs是單程序單執行緒,但是基於V8的強大驅動力,以及事件驅動模型,nodejs的效能非常高,而且想達到多核或者多程序也不是很
JavaScript事件驅動模型
1.事件驅動模型: 注:可以看做使用者點選某個功能或者按鈕,來產生一個onclick事件來觸發某個函式; 2.事件驅動要素: 1.事件源; 2.事件(行為); 例如: 滑鼠點選;
走進nginx事件驅動模型
最近在看阿里陶輝前輩寫的”深入理解nginx”中的nginx的事件模組。之所以想看這塊內容,是因為nginx可以處理龐大的併發連線,想看看支援其背後的事件驅動是如何構建的 這篇博文我也不想貼程式碼什麼的整一堆東西來講述nginx事件驅動,一來我未必理解的那麼透
YARN中MRAppMaster的事件驅動模型與狀態機處理訊息過程的分析
在MRv1中,物件之間的作用關係是基於函式呼叫實現的,當一個物件向另外一個物件傳遞訊息時,會直接採用函式呼叫的方式,並且這個過程是序列的。比如,當TaskTracker需要執行一個Task的時候,將首先下載Task依賴的檔案(JAR包,二進位制檔案等,字典檔案等),然後執行
C# 事件驅動基礎
事件 事件是物件傳送的訊息,以發訊號通知操作的發生。 事件可以由使用者互動引起,例如單擊按鈕,也可能是由某些其他程式的邏輯引發,例如更改的屬性值。 引發事件的物件稱為event sender
【無私分享:ASP.NET CORE 專案實戰(第十四章)】圖形驗證碼的實現
1 public class VierificationCodeServices 2 { 3 /// <summary> 4 /// 該方法用於生成指定位數的隨機數 5 /// </summary> 6
Spring事件驅動模型詳解
事件驅動模型簡介事件驅動模型也就是我們常說的觀察者,或者釋出-訂閱模型;理解它的幾個關鍵點:首先是一種物件間的一對多的關係;最簡單的如交通訊號燈,訊號燈是目標(一方),行人注視著訊號燈(多方);當目標傳送改變(釋出),觀察者(訂閱者)就可以接收到改變;觀察者如何處理(如行人如何走,是快走/慢走/不走,目標不會
spring event的事件驅動模型的最佳實踐@EventListene
我們知道觀察者模式可以實現程式碼的解耦,而spring的event模型就是這種設計模式的極佳體現。一個事件包含:事件釋出、監聽、和事件源。在spring中我們可以通過ApplicationContext的publishEvent方法去釋出事件;通過實現Appl
事件驅動模型例項詳解(Java篇)
這個按鈕的時候,按鈕物件會直接把這次點選感覺傳遞給ButtonEventHandler的actionPerformed方法處理,為養成較好的程式設計習慣,我們中心並不建議學員直接在該委託方法中編寫程式碼,而是需要將該事件處理再次轉發給窗體中的某個方法來處理,這個方法的命名也必須是有規則的,就是事件源名+下劃線
學習貪吃蛇JS專案實戰筆記2
cc.MoveTo._create = cc.MoveTo.create; cc.moveTo = cc.MoveTo.create = function(duration, posOrX, y){ if (undefined === y){ return cc.MoveTo._cr
事件驅動模型和觀察者模式
你有一件事情,做這件事情的過程包含了許多職責單一的子過程。這樣的情況及其常見。當這些子過程有如下特點時,我們應該考慮設計一種合適的框架,讓框架來完成一些業務無關的事情,從而使得各個子過程的開發可以專注於自己的業務。 這些子過程有一定的執行次序; 這些子過程之間需要較靈活