為什麼要學習多執行緒?
- 2010年1月21日是10年某市公務員考試的報名截止日。因從下午2點開始,用於報名的北京市人事考試網癱瘓,原定於昨天下午5點截止的報名時間延遲至今天上午11點。
- 2011年3月11日下午5時(北京時間12日早9點),蘋果釋出新一代的平板電腦產品iPad 2,配備了A5.1Ghz雙核處理器,這寓意著平板電腦和筆記本一同進入”多核時代”。
- 同年6月18日,國內著名B2C—京東在週年慶典之際舉行了”隆重”的大規模的促銷活動,搶購隨之而來,訂單擠爆京東 限時達臨時取消。 昨天有消費者反映,由於點選量過大,18日早上京東商城網站一度癱瘓。一位消費者說:“18日凌晨1點開始就登不上京東商城。”劉強東也表示:由於流量多次超過4個G,伺服器執行緩慢。 昨天,京東商城官網釋出公告稱,“‘618’活動異常火爆且使用者下單速度空前,致使部分使用者已購訂單顯示出現延遲,使用者在一段時間內無法在‘我的京東’中查詢到自己的訂單。目前已購訂單顯示延遲的問題已得到有效解決,對此給您帶來的不便,我們深表歉意。”
- 2015年05月05日登入風信子網上商城發現,首頁除了廣告和相關訊息外,只有“註冊賬號獲取更多優惠”這唯一一個按鈕,沒有商品展示,沒有產品搜尋,不能網上下單,甚至連進入商城的按鈕也沒有。風信子南沙跨境商品直購體驗中心相關負責人表示,這主要是因為預約的人數太多,截至五一,預約人數已超過十萬,太多人頻繁登陸,導致網店伺服器癱瘓,目前技術人員還在努力維修中。該負責人介紹,體驗中心的網站目前正在除錯,“網站目前的作用主要是給市民預約和提前註冊,通過網路註冊的市民不用在現場驗證身份證等資訊,可以提高購買效率。”
下面通過一些例項來認識一下多執行緒和讓大家知道為什麼要學習多執行緒。
寫在前面
老闆只有兩種,好的和壞的。好的老闆只跟你談錢,壞的老闆只跟你談理想。
模擬場景
假設後臺有個monitor時事的在監測訂單,且可以發現訂單然後處理訂單,每次(1次/S)可以處理1千個訂單,需要向倉庫系統發出指令,讓他們負責配送發貨。那麼我們來寫一個EmulationSystem(模擬系統)
JobHelper因為我們只是為了模擬一個環境,所以它是這樣的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
//------------------------------------------------------------------------------ // <copyright file="JobHelper.cs" company="CNBlogs Corporation" owner="請叫我頭頭哥"> // Copyright (C) 2015-2016 All Rights Reserved // 原博文地址: http://www.cnblogs.com/toutou/ // </copyright> //------------------------------------------------------------------------------ namespace CNBlogs.Common.Shared { using System.Threading; /// <summary> /// Job helper /// </summary> public class JobHelper { /// <summary> /// Get job total count /// </summary> /// <returns></returns> public int GetJobCount() { // 我們的側重點是多執行緒,所以我們就模擬每次有1千個訂單,而模擬,所以我們不關心其他的。只要訂單數量。 return 1000; } /// <summary> /// Submit job /// </summary> /// <param name="jobId">For job id</param> /// <returns>Submit job status</returns> public bool SubmitJob(int jobId) { // 假設針對每個訂單向後臺傳送任務需要1秒,而且每個訂單都能成功傳送 Thread.Sleep(1000); return true; } } } |
背景我們也交待完了,現在需要來得到這些訂單資訊以後,向倉庫submit這些訂單。
實戰演習
根據這種背景,我們可以有很多處理的辦法。
-
傳統辦法:
(開啟無腦模式:未使用多執行緒)
程式碼正文:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546//------------------------------------------------------------------------------// <copyright file="Runner.cs" company="CNBlogs Corporation" owner="請叫我頭頭哥">// Copyright (C) 2015-2016 All Rights Reserved// 原博文地址: http://www.cnblogs.com/toutou/// </copyright>//------------------------------------------------------------------------------namespace CNBlogs.EmulationSystem{using System;using System.Threading;using CNBlogs.Common.Shared;class Runner{/// <summary>/// Job helper/// </summary>private static JobHelper jobHelper = new JobHelper();/// <summary>/// Runner lock/// </summary>private static Mutex mutex;static void Main(string[] args){// 記錄開始處理的時間DateTime paddingTime = DateTime.Now.ToLocalTime();int orderCount = jobHelper.GetJobCount();// 用一個指示呼叫執行緒是否應擁有互斥體的初始所屬權的布林值來初始化 Mutex 類的新例項。// 當前程式只能啟動一次mutex = new Mutex(false, "CNBlogs.EmulationSystem");if (!mutex.WaitOne(0, false)){Console.Out.WriteLine("Monitor already running...");return;}for (int i = 0; i < orderCount; i++){// 假設i就是job idjobHelper.SubmitJob(i);}// 記錄處理完成的時間DateTime completeTime = DateTime.Now.ToLocalTime();var minutes = (completeTime - paddingTime).TotalSeconds;Console.WriteLine(string.Format("處理{0}個訂單,花費時間{1}秒", orderCount, minutes));}}}程式碼效果:
PS:現在的這些個電商,動不動來個什麼週年慶店慶什麼雙11雙12的一頓突突,搞得我們這些老百姓就全部蜂擁而上,顯然如果用傳統的方法雖然不會出錯,但是老闆肯定會找你喝茶。在多核時代用這種方法也只能恨鐵不成鋼了。所以這個方法是絕對不可取的。
-
Task方法:
如果有對Task不是很熟悉的園友可以在這裡解開心謎。
程式碼正文:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455//------------------------------------------------------------------------------// <copyright file="Runner.cs" company="CNBlogs Corporation" owner="請叫我頭頭哥">// Copyright (C) 2015-2016 All Rights Reserved// 原博文地址: http://www.cnblogs.com/toutou/// </copyright>//------------------------------------------------------------------------------namespace CNBlogs.EmulationSystem{using System;using System.Collections.Generic;using System.Threading;using System.Threading.Tasks;using CNBlogs.Common.Shared;class Runner{/// <summary>/// Job helper/// </summary>private static JobHelper jobHelper = new JobHelper();/// <summary>/// Runner lock/// </summary>private static Mutex mutex;static void Main(string[] args){// 記錄開始處理的時間DateTime paddingTime = DateTime.Now.ToLocalTime();int orderCount = jobHelper.GetJobCount();// 用一個指示呼叫執行緒是否應擁有互斥體的初始所屬權的布林值來初始化 Mutex 類的新例項。// 當前程式只能啟動一次mutex = new Mutex(false, "CNBlogs.EmulationSystem");if (!mutex.WaitOne(0, false)){Console.Out.WriteLine("Monitor already running...");return;}var taskList = new List<Task>();for (int i = 0; i < orderCount; i++){int temp=i;taskList.Add(Task.Factory.StartNew(() =>{// 假設i就是job idjobHelper.SubmitJob(temp);}));}// 等待所有task執行完畢Task.WaitAll(taskList.ToArray());// 記錄處理完成的時間DateTime completeTime = DateTime.Now.ToLocalTime();var minutes = (completeTime - paddingTime).TotalSeconds;Console.WriteLine(string.Format("Complete: {0},cost {1} s", orderCount, minutes));}}}程式碼效果:
相信分別從有TASK和沒有TASK的這兩次demo中,可以清楚的發現多執行緒處理這些頻繁互動能力的魅力。你會愛上這個方法。確實提高的效率。但是問題也隨之而來,一個程式多執行緒的總數和大小是有要求的(這個取決於伺服器的配置),不是任由我們肆意的開採的。如果訂單太多,我們不控制task那是會拉仇恨的。task需要控制。
-
Semaphore:
如果有對Semaphore不是很熟悉的園友可以在這裡解開心謎。
程式碼正文:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677//------------------------------------------------------------------------------// <copyright file="Runner.cs" company="CNBlogs Corporation" owner="請叫我頭頭哥">// Copyright (C) 2015-2016 All Rights Reserved// 原博文地址: http://www.cnblogs.com/toutou/// </copyright>//------------------------------------------------------------------------------namespace CNBlogs.EmulationSystem{using System;using System.Collections.Generic;using System.Threading;using System.Threading.Tasks;using CNBlogs.Common.Shared;class Runner{/// <summary>/// Job helper/// </summary>private static JobHelper jobHelper = new JobHelper();/// <summary>/// The locker used to lock the semaphore and thread./// </summary>private static object lockerObj = new object();/// <summary>/// The semaphore limit the thread number of get latest test info/// </summary>private static Semaphore semaphoreLimit;/// <summary>/// Runner lock/// </summary>private static Mutex mutex;static void Main(string[] args){// 記錄開始處理的時間DateTime paddingTime = DateTime.Now.ToLocalTime();int orderCount = jobHelper.GetJobCount();// 用一個指示呼叫執行緒是否應擁有互斥體的初始所屬權的布林值來初始化 Mutex 類的新例項。// 當前程式只能啟動一次mutex = new Mutex(false, "CNBlogs.EmulationSystem");if (!mutex.WaitOne(0, false)){Console.Out.WriteLine("Monitor already running...");return;}// 假設我們的伺服器一個程式只能接受的大小=當前執行緒大小*500 ps:500是設定訊號量的最大值int maxProcNumber = 500;using (semaphoreLimit = new Semaphore(0, maxProcNumber)){// 以指定的次數退出訊號量並返回前一個計數。semaphoreLimit.Release(maxProcNumber);var taskList = new List<Task>();for (int i = 0; i < orderCount; i++){int temp=i;// 如果SubmitJob有IO或者其他容易因為衝突而引起異常的話,這裡需要加上lock//lock (lockerObj)//{semaphoreLimit.WaitOne();taskList.Add(Task.Factory.StartNew(() =>{// 假設i就是job idjobHelper.SubmitJob(temp);// 退出訊號量並返回前一個計數。semaphoreLimit.Release();}));//}}// 等待所有task執行完畢Task.WaitAll(taskList.ToArray());}// 記錄處理完成的時間DateTime completeTime = DateTime.Now.ToLocalTime();var minutes = (completeTime - paddingTime).TotalSeconds;Console.WriteLine(string.Format("Complete: {0},cost {1} s", orderCount, minutes));}}}程式碼效果:
部落格總結
多執行緒路還很長…