C#線程學習筆記二:線程池中的工作者線程

来源:https://www.cnblogs.com/atomy/archive/2019/12/04/11946785.html
-Advertisement-
Play Games

本筆記摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/ThreadPool.html,記錄一下學習過程以備後續查用。 一、線程池基礎 首先,創建和銷毀線程是一個要耗費大量時間的過程,其次,太多的線程也會浪費記憶體資源,所以通過Thread類來創 ...


    本筆記摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/ThreadPool.html,記錄一下學習過程以備後續查用。

    一、線程池基礎

    首先,創建和銷毀線程是一個要耗費大量時間的過程,其次,太多的線程也會浪費記憶體資源,所以通過Thread類來創建過多的線程反而有損於性能。為了改善這樣

的問題 ,.NET中就引入了線程池。

    線程池形象的表示就是存放應用程式中使用的線程的一個集合(就是放線程的地方,這樣線程都放在一個地方就好管理了)。

    CLR初始化時,線程池中是沒有線程的,在內部, 線程池維護了一個操作請求隊列。當應用程式想執行一個非同步操作時,就調用一個方法,將一個任務放到線程池

的隊列中,線程池代碼從隊列中提取任務,將這個任務委派給一個線程池線程去執行,當線程池線程完成任務時,線程不會被銷毀,而是返回到線程池中,等待響應另

一個請求。由於線程不被銷毀, 這樣就可以避免因為創建線程所產生的性能損失。

    MSDN表述:

    “線程池經常用在伺服器應用程式中,每一個新進來的需求被分配給一個線程池中的線程,這樣該需求能被非同步的執行,沒有阻礙主線程或推遲後繼需求的處理。”

    註意:通過線程池創建的線程預設為後臺線程,優先順序預設為Normal。

    二、通過線程池的工作者線程實現非同步

    2.1創建工作者線程的方法

    public static bool QueueUserWorkItem (WaitCallback callback);

    public static bool QueueUserWorkItem(WaitCallback callback, Object state);

    這兩個方法向線程池的隊列添加一個工作項(work item)以及一個可選的狀態數據,然後,這兩個方法就會立即返回。

    工作項其實就是由callback參數標識的一個方法,該方法將由線程池線程執行。同時寫的回調方法必須匹配System.Threading.WaitCallback委托類型,定義為:

    public delegate void WaitCallback(Object state);

    下麵演示如何通過線程池線程來實現非同步調用:

    class Program
    {
        static void Main(string[] args)
        {
            #region 通過線程池的工作者線程實現非同步
            //設置線程池中工作者線程最大數量為1000,I/O線程最大數量為1000。
            ThreadPool.SetMaxThreads(1000, 1000);
            Console.WriteLine("Main thread: queue an asynchronous method.");
            PrintMessage("Main thread start.");

            //把工作項添加到隊列中,此時線程池會用工作者線程去執行回調方法。
            ThreadPool.QueueUserWorkItem(AsyncMethod);
            Console.Read();
            #endregion
        }

        /// <summary>
        /// 列印線程池信息
        /// </summary>
        /// <param name="data"></param>
        private static void PrintMessage(string data)
        {
            // 獲得線程池中可用的工作者線程數量及I/O線程數量
            ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber);

            Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workThreadNumber.ToString(),
                ioThreadNumber.ToString());
        }

        /// <summary>
        /// 非同步方法:必須匹配WaitCallback委托
        /// </summary>
        /// <param name="state"></param>
        private static void AsyncMethod(object state)
        {
            Thread.Sleep(1000);
            PrintMessage("Asynchoronous method.");
            Console.WriteLine("Asynchoronous thread has worked.");
        }
    }

    運行結果如下:

    從結果中可以看出,線程池中的可用的工作者線程少了一個,用去執行回調方法了。

    ThreadPool.QueueUserWorkItem(WaitCallback callback,Object state) 方法可以把object對象作為參數傳送到回調函數中,使用方法與

ThreadPool.QueueUserWorkItem(WaitCallback callback)類似,這裡就不列出了。

    2.2 協作式取消

    .NET Framework提供了取消操作的模式, 這個模式是協作式的。為了取消一個操作,首先必須創建一個System.Threading.CancellationTokenSource對象。

    下麵代碼演示協作式取消的使用,主要實現當用戶在控制台敲下回車鍵後就停止數數方法。

    class Program
    {
        static void Main(string[] args)
        {
            #region 協作式取消
            ThreadPool.SetMaxThreads(1000, 1000);
            Console.WriteLine("Main thread run.");
            PrintMessage("Start");
            Run();
            Console.ReadKey();
            #endregion
        }

        /// <summary>
        /// 列印線程池信息
        /// </summary>
        /// <param name="data"></param>
        private static void PrintMessage(string data)
        {
            //獲得線程池中可用的工作者線程數量及I/O線程數量
            ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber);

            Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workThreadNumber.ToString(),
                ioThreadNumber.ToString());
        }

        /// <summary>
        /// 運行工作者線程(包含協作式取消)
        /// </summary>
        private static void Run()
        {
            CancellationTokenSource cts = new CancellationTokenSource();

            //這裡是用Lambda表達式的寫法,效果一樣。
            //ThreadPool.QueueUserWorkItem(obj => Count(cts.Token, 1000));

            ThreadPool.QueueUserWorkItem(Callback, cts.Token);
            Console.WriteLine("Press enter key to cancel the operation.\n");
            Console.ReadLine();
            //傳達取消請求
            cts.Cancel();
        }

        /// <summary>
        /// 回調函數
        /// </summary>
        /// <param name="state"></param>
        private static void Callback(object state)
        {
            Thread.Sleep(1000);
            PrintMessage("Asynchoronous method start.");
            CancellationToken token = (CancellationToken)state;
            Count(token, 1000);
        }

        /// <summary>
        /// 數數
        /// </summary>
        /// <param name="token"></param>
        /// <param name="countTo"></param>
        private static void Count(CancellationToken token, int countTo)
        {
            for (int i = 1; i <= countTo; i++)
            {
                if (token.IsCancellationRequested)
                {
                    Console.WriteLine("Count is canceled.");
                    break;
                }

                Console.WriteLine(i);
                Thread.Sleep(300);
            }
            Console.WriteLine("Count has done.");
        }
    }

    運行結果如下:

    三、使用委托實現非同步

    涉及術語解釋--非同步編程模型:

    APM 非同步編程模型(Asynchronous Programming Model)

    EAP 基於事件的非同步編程模式(Event-based Asynchronous Pattern)

    TAP 基於任務的非同步編程模式(Task-based Asynchronous Pattern)

    通過調用ThreadPool的QueueUserWorkItem方法來來啟動工作者線程非常方便,但委托WaitCallback指向的是帶有一個參數的無返回值的方法。如果我們實際操作中

需要有返回值,或者需要帶有多個參數, 這時通過這樣的方式就難以實現了。 為瞭解決這樣的問題,我們可以通過委托來建立工作這線程。

    下麵代碼演示使用委托實現非同步:

    class Program
    {
        //使用委托實現非同步,是使用了非同步編程模型APM。
        private delegate string ThreadDelegate();

        static void Main(string[] args)
        {
            #region 使用委托實現非同步
            ThreadPool.SetMaxThreads(1000, 1000);
            PrintMessage("Main thread start.");

            //實例化委托
            ThreadDelegate threadDelegate = new ThreadDelegate(AsyncMethod);
            //非同步調用委托
            IAsyncResult result = threadDelegate.BeginInvoke(null, null);
            //獲取結果並列印
            string returnData = threadDelegate.EndInvoke(result);
            Console.WriteLine(returnData);
            Console.ReadLine();
            #endregion
        }

        /// <summary>
        /// 非同步方法
        /// </summary>
        /// <returns></returns>
        private static string AsyncMethod()
        {
            Thread.Sleep(1000);
            PrintMessage("Asynchoronous method.");
            return "Method has completed.";
        }
    }

    運行結果如下:

    四、任務

    同樣,任務的引入也是為瞭解決通過ThreadPool.QueueUserWorkItem中限制的問題。

    下麵代碼演示通過任務來實現非同步:

    4.1 使用任務來實現非同步

    class Program
    {
        static void Main(string[] args)
        {
            #region 使用任務實現非同步
            ThreadPool.SetMaxThreads(1000, 1000);
            PrintMessage("Main thread start.");
            //調用構造函數創建Task對象
            Task<int> task = new Task<int>(n => AsyncMethod((int)n), 10);

            //啟動任務 
            task.Start();
            //等待任務完成
            task.Wait();
            Console.WriteLine("The method result is: " + task.Result);
            Console.ReadLine();
            #endregion
        }

        /// <summary>
        /// 非同步方法
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        private static int AsyncMethod(int n)
        {
            Thread.Sleep(1000);
            PrintMessage("Asynchoronous method.");

            int sum = 0;
            for (int i = 1; i < n; i++)
            {
                //運算溢出檢查
                checked
                {
                    sum += i;
                }
            }

            return sum;
        }
    }

    運行結果如下:

    4.2 取消任務

    如果要取消任務, 同樣也可以CancellationTokenSource對象來取消。

    下麵代碼演示取消一個任務:

    class Program
    {
        static void Main(string[] args)
        {
            #region 取消任務
            ThreadPool.SetMaxThreads(1000, 1000);
            PrintMessage("Main thread start.");
            CancellationTokenSource cts = new CancellationTokenSource();

            //調用構造函數創建Task對象,將一個CancellationToken傳給Task構造器從而使Task和CancellationToken關聯起來。
            Task<int> task = new Task<int>(n => AsyncMethod(cts.Token, (int)n), 10);

            //啟動任務 
            task.Start();
            //延遲取消任務
            Thread.Sleep(3000);

            //取消任務
            cts.Cancel();
            Console.WriteLine("The method result is: " + task.Result);
            Console.ReadLine();
            #endregion
        }

        /// <summary>
        /// 非同步方法
        /// </summary>
        /// <param name="ct"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        private static int AsyncMethod(CancellationToken ct, int n)
        {
            Thread.Sleep(1000);
            PrintMessage("Asynchoronous method.");

            int sum = 0;
            try
            {
                for (int i = 1; i < n; i++)
                {
                    //當CancellationTokenSource對象調用Cancel方法時,就會引起OperationCanceledException異常,
                    //通過調用CancellationToken的ThrowIfCancellationRequested方法來定時檢查操作是否已經取消,
                    //這個方法和CancellationToken的IsCancellationRequested屬性類似。
                    ct.ThrowIfCancellationRequested();
                    Thread.Sleep(500);
                    //運算溢出檢查
                    checked
                    {
                        sum += i;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception is:" + e.GetType().Name);
                Console.WriteLine("Operation is canceled.");
            }

            return sum;
        }
    }

    運算結果如下:

    4.3 任務工廠

    同樣也可以通過任務工廠TaskFactory類型來實現非同步操作。

 

    class Program
    {
        static void Main(string[] args)
        {
            #region 使用任務工廠實現非同步
            ThreadPool.SetMaxThreads(1000, 1000);
            Task.Factory.StartNew(() => PrintMessage("Main thread."));
            Console.Read();
            #endregion
        }

        /// <summary>
        /// 列印線程池信息
        /// </summary>
        /// <param name="data"></param>
        private static void PrintMessage(string data)
        {
            //獲得線程池中可用的工作者線程數量及I/O線程數量
            ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber);

            Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
                data,
                Thread.CurrentThread.ManagedThreadId,
                Thread.CurrentThread.IsBackground.ToString(),
                workThreadNumber.ToString(),
                ioThreadNumber.ToString());
        }
    }

    運行結果如下:

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 上面這種方法“無論如何”都讀不出f的內容,使用readlines和迴圈也不行。 但是,用以下的方法,卻可以“正常讀取”: 這是為什麼呢? PS:遇到問題沒人解答?需要Python學習資料?可以加點擊下方鏈接自行獲取 note.youdao.com/noteshare?id=2dce86d0c2588 ...
  • 使用VSCode + NET Core3.0在ASP.NET Core中使用Web API創建 RESTful 服務,包括創建簡單Rest API、格式化輸出、JSON Patch請求、Open API(Swagger)集成 ...
  • asp.net core 從 3.0 到 3.1 Intro 今天 .net core 3.1 正式發佈了,.net core 3.1 正式版已發佈,3.1 主要是對 3.0 的 bug 修複,以及一些小優化,而且作為 LTS 版本,建議大家升級。值得一提的是.net core 2.2 這個月就要壽 ...
  • 目 錄 1. 概述... 2 2. 演示信息... 2 3. 簡單介紹... 3 4. 產品特點... 4 5. 價值體現... 5 1. 概述 經過一段時間的努力,iNeuDA產品組件已經開發和測試完成,現在正式上線。現在iNeuOS工業互聯網操作系統的技術體系和產品體系更佳完善,為中小企業提供更 ...
  • Net Core 2.x 跟 Net Core3.0 還是有很大的區別的,隨著.NET Core 3.1發佈,也就意味著老版本慢慢的要停止維護。 Net Core 3.0 其實就是過渡版本,用於我們練手。 ASP.NET Core 2.2 遷移到3.0:https://docs.microsoft. ...
  • 參考博客: https://blog.csdn.net/flyer_tang/article/details/80320974 https://blog.csdn.net/weixin_30773135/article/details/97923338 官網下載地址: http://redis.io ...
  • java虛擬機具有語言無關係,它只和“class文件“這種特定的二進位文件格式綁定。 不同語言的編譯器將對應的程式編譯成位元組碼文件(*.class),送給jvm執行。 2.1、魔數(是否可以被虛擬機執行)和class文件版本 2.2、常量池 2.3、訪問標誌(識別訪問信息) 2.4、類索引、父類索引 ...
  • 本文將介紹領域驅動設計(DDD)戰術模式中另一個常見且非常重要的概念 - 實體。相對戰術模式中其他的一些概念(例如 值對象、領域服務等)來說,實體應該比較容易讓人理解和運用。但是我們如何去發現所在領域中的實體呢?如何保證建立的實體是富含行為的?實體運用時又有那些註意的細節呢?本文將從不同的角度來帶大... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...