程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C# 多線程,論多核時代愛恨情仇,

C# 多線程,論多核時代愛恨情仇,

編輯:C#入門知識

C# 多線程,論多核時代愛恨情仇,


為什麼要學習多線程?

  • 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日登錄風信子網上商城發現,首頁除了廣告和相關消息外,只有“注冊賬號獲取更多優惠”這唯一一個按鈕,沒有商品展示,沒有產品搜索,不能網上下單,甚至連進入商城的按鈕也沒有。風信子南沙跨境商品直購體驗中心相關負責人表示,這主要是因為預約的人數太多,截至五一,預約人數已超過十萬,太多人頻繁登陸,導致網店服務器癱瘓,目前技術人員還在努力維修中。該負責人介紹,體驗中心的網站目前正在調試,“網站目前的作用主要是給市民預約和提前注冊,通過網絡注冊的市民不用在現場驗證身份證等信息,可以提高購買效率。”

下面通過一些實例來認識一下多線程和讓大家知道為什麼要學習多線程。

C# 多線程

v寫在前面

老板只有兩種,好的和壞的。好的老板只跟你談錢,壞的老板只跟你談理想。

v模擬場景

假設後台有個monitor時事的在監測訂單,且可以發現訂單然後處理訂單,每次(1次/S)可以處理1千個訂單,需要向倉庫系統發出指令,讓他們負責配送發貨。那麼我們來寫一個EmulationSystem(模擬系統)

C# 多線程

JobHelper因為我們只是為了模擬一個環境,所以它是這樣的。

//------------------------------------------------------------------------------
// <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這些訂單。

v實戰演習

根據這種背景,我們可以有很多處理的辦法。

  • 傳統辦法:

    (開啟無腦模式:未使用多線程)

    代碼正文:

    //------------------------------------------------------------------------------
    // <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 id
                    jobHelper.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不是很熟悉的園友可以在這裡解開心謎。

    代碼正文:

    //------------------------------------------------------------------------------
    // <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 id jobHelper.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)); } } }

    代碼效果:

    C# 多線程

    相信分別從有TASK和沒有TASK的這兩次demo中,可以清楚的發現多線程處理這些頻繁交互能力的魅力。你會愛上這個方法。確實提高的效率。但是問題也隨之而來,一個進程多線程的總數和大小是有要求的(這個取決於服務器的配置),不是任由我們肆意的開采的。如果訂單太多,我們不控制task那是會拉仇恨的。task需要控制。

    C# 多線程

  • Semaphore:

    如果有對Semaphore不是很熟悉的園友可以在這裡解開心謎。

    代碼正文:

    //------------------------------------------------------------------------------
    // <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 id jobHelper.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)); } } }

    代碼效果:

v博客總結

多線程路還很長...

 


作  者:請叫我頭頭哥
出  處:http://www.cnblogs.com/toutou/
關於作者:專注於微軟平台的項目開發。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信我
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved