別嘲笑這個標題。我想了很久。有點“投機取巧”的功效吧!
程序當然不能做飯。
之前的我們的系列文章,介紹, 多線程執行,任務派發。定時器執行。腳本加載。程序狀態機。
這些都是零零散散,或者說都是模塊化介紹,以及模塊測試用例。
那麼今天我們就來模擬正常程序流程。使用上述的功能性代碼完成流程。
當然今天的測試用例程序肯定和做飯有關。今天要做的是模擬一個餐廳的流程。
完成 客人入座 -> 點菜 -> 等待就餐 -> 就餐 -> 等待結賬 -> 結賬 -> 離開.
期間包括 等待就餐 添加茶水,就餐的添加茶水,添加米飯等隨機事件
新建控制台項目:
Sz.Network.DiningRoom 用於存放主文件項目
類庫
Sz.Network.DiningRoom.Scripts 用於存放腳本文件項目
我們先來初始化餐廳。
/** * * @author 失足程序員 * @Blog http://www.cnblogs.com/ty408/ * @mail [email protected] * @phone 13882122019 * */ namespace Sz.Network.DiningRoom { /// <summary> /// /// </summary> public class 餐廳 { private static 餐廳 instance = new 餐廳(); public static 餐廳 GetInstance { get { return instance; } } public long 全局線程 = 0; public long 廚師s = 0; public long 傳菜員s = 0; public long 服務員s = 0; public long 配菜員s = 0; public long 收銀員s = 0; public long 洗菜員s = 0; public 客人[] table = null; public void Init(int tableSize) { Logger.Info("初始化餐廳"); //所有的工作人員都是一個線程 全局線程 = ThreadPool.ThreadManager.GetInstance.GetThreadModel(new ThreadPool.ThreadModel("全局線程", 1)); //所有的工作人員都是一個線程 廚師s = ThreadPool.ThreadManager.GetInstance.GetThreadModel(new ThreadPool.ThreadModel("廚師", 3)); //所有的工作人員都是一個線程 傳菜員s = ThreadPool.ThreadManager.GetInstance.GetThreadModel(new ThreadPool.ThreadModel("傳菜員", 5)); //所有的工作人員都是一個線程 服務員s = ThreadPool.ThreadManager.GetInstance.GetThreadModel(new ThreadPool.ThreadModel("服務員", 5)); //所有的工作人員都是一個線程 配菜員s = ThreadPool.ThreadManager.GetInstance.GetThreadModel(new ThreadPool.ThreadModel("配菜員", 3)); //所有的工作人員都是一個線程 收銀員s = ThreadPool.ThreadManager.GetInstance.GetThreadModel(new ThreadPool.ThreadModel("收銀員", 1)); //所有的工作人員都是一個線程 洗菜員s = ThreadPool.ThreadManager.GetInstance.GetThreadModel(new ThreadPool.ThreadModel("洗菜員", 2)); table = new 客人[tableSize]; for (int i = 0; i < tableSize; i++) { Logger.Info("初始化餐桌 " + (i + 1) + " 號桌"); } } } }
每一個工作人員都是一個線程。模擬線程。
我們這裡,餐廳配置:"廚師", 3 "傳菜員", 5 "服務員", 5 "配菜員", 3 "收銀員", 1 "洗菜員", 2
各個環節的人員都不相同,且每一步操作都不進相同。
接下來我們初始化客人,
/** * * @author 失足程序員 * @Blog http://www.cnblogs.com/ty408/ * @mail [email protected] * @phone 13882122019 * */ namespace Sz.Network.DiningRoom { public class 客人 { public static EnumStatus Status入座 = new EnumStatus(1 << 0, 0x000000); public static EnumStatus Status取消 = new EnumStatus(1 << 1, 0x000000); public static EnumStatus Status點菜 = new EnumStatus(1 << 2, 0x000000); public static EnumStatus Status就餐 = new EnumStatus(1 << 3, 0x000000); public static EnumStatus Status結賬中 = new EnumStatus(1 << 4, 0x000000); public static EnumStatus Status等待就餐 = new EnumStatus(1 << 5, 0x000000); public static EnumStatus Status等待結賬 = new EnumStatus(1 << 6, 0x000000); /// <summary> /// 存儲臨時數據的 /// </summary> public ObjectAttribute TempAttribute = new ObjectAttribute(); /// <summary> /// 客人當前的狀態 /// </summary> public EnumStatus Staus = new EnumStatus(0, 0x000000); public List<菜肴> 菜肴s = new List<菜肴>(); public int TableID { get; set; } /// <summary> /// 每一個客人的隨機標識 /// </summary> public string guidID { get; set; } public 客人(int tableID) { guidID = Guid.NewGuid().ToString().Replace("-", ""); this.TableID = tableID; Staus |= Status入座; Show(); } public void 點菜() { ThreadPool.ThreadManager.GetInstance.AddTask(餐廳.GetInstance.服務員s, new Task點菜(this)); Task隨機事件發生處理器 task = new Task隨機事件發生處理器(this.TableID + " 號桌客人 上碗筷"); ThreadPool.ThreadManager.GetInstance.AddTask(餐廳.GetInstance.服務員s, task); } public void Add點菜(菜肴 菜) { 菜肴s.Add(菜); ThreadPool.ThreadManager.GetInstance.AddTask(餐廳.GetInstance.洗菜員s, new Task菜(this, 菜)); } public void Show() { string 狀態 = ""; if (Staus.HasFlag(Status入座)) { 狀態 = "入座"; } else if (Staus.HasFlag(Status取消)) { 狀態 = "取消"; } else if (Staus.HasFlag(Status點菜)) { 狀態 = "點菜"; } else if (Staus.HasFlag(Status等待就餐)) { 狀態 = "等待就餐"; } else if (Staus.HasFlag(Status就餐)) { 狀態 = "就餐"; } else if (Staus.HasFlag(Status等待結賬)) { 狀態 = "等待結賬"; } else if (Staus.HasFlag(Status結賬中)) { 狀態 = "結賬中"; } Logger.Info(this.TableID + " 號桌子 客人 " + this.guidID + " 當前狀態:" + 狀態); } } }
初始化菜肴
/** * * @author 失足程序員 * @Blog http://www.cnblogs.com/ty408/ * @mail [email protected] * @phone 13882122019 * */ namespace Sz.Network.DiningRoom { public class 菜肴 { public static EnumStatus Status點菜 = new EnumStatus(1 << 0, 0x000000); public static EnumStatus Status取消 = new EnumStatus(1 << 1, 0x000000); public static EnumStatus Status洗菜 = new EnumStatus(1 << 2, 0x000000); public static EnumStatus Status配菜 = new EnumStatus(1 << 3, 0x000000); public static EnumStatus Status炒菜 = new EnumStatus(1 << 4, 0x000000); public static EnumStatus Status傳菜 = new EnumStatus(1 << 5, 0x000000); public static EnumStatus Status就餐 = new EnumStatus(1 << 6, 0x000000); public static EnumStatus Status結束就餐 = new EnumStatus(1 << 7, 0x000000); public string Name { get; private set; } public EnumStatus Staus = new EnumStatus(0, 0x000000); /// <summary> /// 存儲臨時數據的 /// </summary> public ObjectAttribute TempAttribute = new ObjectAttribute(); public 菜肴(string name) { this.Name = name; Staus |= Status點菜; Show(); } public void Show() { string 狀態 = ""; if (Staus.HasFlag(Status點菜)) { 狀態 = "點菜"; } else if (Staus.HasFlag(Status取消)) { 狀態 = "取消"; } else if (Staus.HasFlag(Status洗菜)) { 狀態 = "洗菜"; } else if (Staus.HasFlag(Status配菜)) { 狀態 = "配菜"; } else if (Staus.HasFlag(Status炒菜)) { 狀態 = "炒菜"; } else if (Staus.HasFlag(Status傳菜)) { 狀態 = "傳菜"; } else if (Staus.HasFlag(Status就餐)) { 狀態 = "就餐"; } Logger.Info(this.Name + " 當前狀態:" + 狀態); } } }
我們需要創建一個定時器任務,對餐桌和客人進行狀態監測和隨機事件發生器
/** * * @author 失足程序員 * @Blog http://www.cnblogs.com/ty408/ * @mail [email protected] * @phone 13882122019 * */ namespace Sz.Network.DiningRoom { public class TimerTask : ThreadPool.TimerTask { /// <summary> /// 間隔 5000 毫秒執行一次 /// </summary> public TimerTask() : base(餐廳.GetInstance.全局線程, 2000) { } public override void Run() { IEnumerable<IScript餐桌檢查器> checkScripts = LoadScriptPool.LoadScriptManager.GetInstance.GetInstances<IScript餐桌檢查器>(); foreach (var item in checkScripts) { item.Run(); } } } }
由於我們餐桌檢查器是一個不定數,所以需要放到腳本去。方便更新程序代碼。
在腳本項目裡面創建腳本文件
/** * * @author 失足程序員 * @Blog http://www.cnblogs.com/ty408/ * @mail [email protected] * @phone 13882122019 * */ namespace Sz.Network.DiningRoom.Scripts { public class Script餐桌檢查器 : IScript餐桌檢查器 { Random random = new Random(DateTime.Now.Millisecond); public Script餐桌檢查器() { } public void Run() { Logger.Info("==================================Script餐桌檢查器======================================="); for (int i = 0; i < 餐廳.GetInstance.table.Length; i++) { if (餐廳.GetInstance.table[i] == null) { int randomValue = random.Next(10000); if (randomValue < 5000) { 客人 客 = new 客人(i + 1); 餐廳.GetInstance.table[i] = 客; } } else { 客人 客 = 餐廳.GetInstance.table[i]; if (客.Staus.HasFlag(客人.Status入座)) { ///如果客人剛剛入座,執行點菜,移交給服務員 客.Staus |= 客人.Status點菜; 客.點菜(); } else if (客.Staus.HasFlag(客人.Status等待就餐)) { bool isFor = true; foreach (var item in 客.菜肴s) { if (!item.Staus.HasFlag(菜肴.Status就餐)) { isFor = false; break; } } if (isFor) { 客.Staus |= 客人.Status就餐; //模擬客人吃飯需要30到50秒 客.TempAttribute["Status就餐"] = SzExtensions.CurrentTimeMillis() + (random.Next(3, 6)) * 10 * 1000; } else { //模擬隨機事件 int randomValue = random.Next(10000); if (randomValue < 6000) { Logger.Info("隨機事件發生 " + (i + 1) + " 號桌客人 添加茶水"); Task隨機事件發生處理器 task = new Task隨機事件發生處理器((i + 1) + " 號桌客人 添加茶水"); ThreadPool.ThreadManager.GetInstance.AddTask(餐廳.GetInstance.服務員s, task); } } } else if (客.Staus.HasFlag(客人.Status就餐)) { if (客.TempAttribute.GetlongValue("Status就餐") < SzExtensions.CurrentTimeMillis()) { 客.Staus |= 客人.Status等待結賬; } else { //模擬隨機事件 string msg = ""; int randomValue = random.Next(10000); if (randomValue < 3000) { msg = " 添加米飯"; } else if (randomValue < 6000) { msg = " 添加茶水"; } if (!string.IsNullOrWhiteSpace(msg)) { Logger.Info("隨機事件發生 " + (i + 1) + " 號桌客人 " + msg); Task隨機事件發生處理器 task = new Task隨機事件發生處理器((i + 1) + " 號桌客人 " + msg); ThreadPool.ThreadManager.GetInstance.AddTask(餐廳.GetInstance.服務員s, task); } } } else if (客.Staus.HasFlag(客人.Status等待結賬)) { 客.Staus |= 客人.Status結賬中; } else if (客.Staus.HasFlag(客人.Status結賬中)) { Logger.Info((i + 1) + " 號桌客人 結束就餐 送走客人"); 餐廳.GetInstance.table[i] = null; return; } 客.Show(); } } } } }
點菜也同樣為方便程序更新,代碼放在腳本執行
/** * * @author 失足程序員 * @Blog http://www.cnblogs.com/ty408/ * @mail [email protected] * @phone 13882122019 * */ namespace Sz.Network.DiningRoom.Scripts { public class Script點菜 : IScript點菜 { public Script點菜() { } public void Run(客人 客) { List<string> 菜肴_葷菜s = new List<string>() { "回鍋肉", "青椒肉絲", "東坡肘子", "糖醋排骨", "魚香肉絲" }; List<string> 菜肴_素菜s = new List<string>() { "空心菜", "鳳尾", "素炒竹筍", "白油絲瓜" }; List<string> 菜肴_湯s = new List<string>() { "番茄煎蛋湯", "紫菜蛋花湯", "酸菜粉絲湯", "素菜湯", "肉片湯" }; Random random = new Random(DateTime.Now.Millisecond); { //int 數量 = random.Next(1, 菜肴_葷菜s.Count); int 數量 = 1; for (int i = 0; i < 數量; i++) { int index = random.Next(菜肴_葷菜s.Count); string name = 菜肴_葷菜s[index]; 菜肴_葷菜s.RemoveAt(index); 菜肴 菜 = new 菜肴(name); 客.Add點菜(菜); } } { //int 數量 = random.Next(1, 菜肴_素菜s.Count); int 數量 = 1; for (int i = 0; i < 數量; i++) { int index = random.Next(菜肴_素菜s.Count); string name = 菜肴_素菜s[index]; 菜肴_素菜s.RemoveAt(index); 菜肴 菜 = new 菜肴(name); 客.Add點菜(菜); } } { //int 數量 = random.Next(1, 菜肴_湯s.Count); int 數量 = 1; for (int i = 0; i < 數量; i++) { int index = random.Next(菜肴_湯s.Count); string name = 菜肴_湯s[index]; 菜肴_湯s.RemoveAt(index); 菜肴 菜 = new 菜肴(name); 客.Add點菜(菜); } } 客.Staus |= 客人.Status等待就餐; } } }
接下來,就是菜的流程任務執行器
/** * * @author 失足程序員 * @Blog http://www.cnblogs.com/ty408/ * @mail [email protected] * @phone 13882122019 * */ namespace Sz.Network.DiningRoom { public class Task菜 : ThreadPool.TaskModel { public 客人 客 { get; set; } public 菜肴 _菜肴 { get; set; } public Task菜(客人 客, 菜肴 _菜肴) { this.客 = 客; this._菜肴 = _菜肴; } public override void Run() { Random random = new Random(DateTime.Now.Millisecond); string 事件 = ""; EnumStatus tempStatus = null; long threadID = 0; if (_菜肴.Staus.HasFlag(菜肴.Status點菜)) { 事件 = "洗菜"; tempStatus = 菜肴.Status洗菜; threadID = 餐廳.GetInstance.洗菜員s; } else if (_菜肴.Staus.HasFlag(菜肴.Status取消)) { 事件 = "取消"; tempStatus = 菜肴.Status取消; } else if (_菜肴.Staus.HasFlag(菜肴.Status洗菜)) { 事件 = "配菜"; tempStatus = 菜肴.Status配菜; threadID = 餐廳.GetInstance.配菜員s; } else if (_菜肴.Staus.HasFlag(菜肴.Status配菜)) { 事件 = "炒菜"; tempStatus = 菜肴.Status炒菜; threadID = 餐廳.GetInstance.廚師s; } else if (_菜肴.Staus.HasFlag(菜肴.Status炒菜)) { 事件 = "傳菜"; tempStatus = 菜肴.Status傳菜; threadID = 餐廳.GetInstance.傳菜員s; } else { return; } int timer = random.Next(2000, 5000); ///模擬耗時 Thread.Sleep(timer); ///修改菜肴的狀態 this._菜肴.Staus |= tempStatus; Logger.Info(Thread.CurrentThread.Name + " " + 客.TableID + " 號桌 客人 " + this._菜肴.Name +" "+ 事件 + " 耗時:" + timer); if (this._菜肴.Staus.HasFlag(菜肴.Status傳菜)) { ///修改菜肴的狀態 this._菜肴.Staus |= 菜肴.Status就餐; } if (threadID > 0) { //移交到下一個工作人員(線程) ThreadPool.ThreadManager.GetInstance.AddTask(threadID, this); } } } }
我們修改一下餐廳的 init 方法
//加載腳本 LoadScriptPool.LoadScriptManager.GetInstance.LoadCSharpFile(new string[] { @"..\..\..\Sz.Network.DiningRoom.Scripts\" }); //初始化定時器任務 ThreadPool.ThreadManager.GetInstance.AddTimerTask(new TimerTask());
菜肴的流程,交給了 Task菜 類處理,菜肴的狀態值修改也是要交給 Task菜 修改的,保證了在同一線程修改狀態值就保證狀態值的正常。
既然說了要有隨機事件發生,那肯定少不了隨機事件的處理器
/** * * @author 失足程序員 * @Blog http://www.cnblogs.com/ty408/ * @mail [email protected] * @phone 13882122019 * */ namespace Sz.Network.DiningRoom { public class Task隨機事件發生處理器 : ThreadPool.TaskModel { string msg; public Task隨機事件發生處理器(string msg) { this.msg = msg; } public override void Run() { Random random = new Random(DateTime.Now.Millisecond); int timer = random.Next(2000, 5000); //模擬隨機事件耗時 Thread.Sleep(timer); Logger.Info(Thread.CurrentThread.Name + " 處理隨機事件發生 " + msg + " 耗時:" + timer); } } }
這樣我們就能啟動程序了測試一下了。
整個流程就是,客人入座,點菜,,被分配到到洗菜,配菜,炒菜,傳菜。就餐。結賬。等一系列流程。
由於人員配置不同,具體工作耗時不同,所以一切都發生都是不定項;
每一個操作在移交給下一個工作者(線程)都是不定操作。而每一個工作者(線程)都有先來後到的原則進行自己工作的處理;
我們未來方便測試和看清楚執行流程。我們只開啟一個餐桌;
餐廳.GetInstance.Init(1);
我的程序真的能做飯哦~!
不知道,這樣講,是否能幫你講明白呢???
老規矩,全套源碼奉獻 svn 地址 http://code.taobao.org/svn/flynetwork_csharp/trunk/Flynetwork/BlogTest
跪求保留
/** * * @author 失足程序員 * @Blog http://www.cnblogs.com/ty408/ * @mail [email protected] * @phone 13882122019 * */