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
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...