併發編程 ---為何要線程池化

来源:https://www.cnblogs.com/pandefu/archive/2023/07/18/17536277.html
-Advertisement-
Play Games

## 引言 眾所周知,使用線程可以極大的提高應用程式的效率和響應性,提高用戶體驗,但是不可以無節制的使用線程,為什麼呢? ## 線程的開銷 線程的開銷實際上是非常大的,我們從空間開銷和時間開銷上分別討論。 ### 線程的空間開銷 線程的空間開銷來自這四個部分: 1. 線程內核對象(Thread Ke ...


引言

眾所周知,使用線程可以極大的提高應用程式的效率和響應性,提高用戶體驗,但是不可以無節制的使用線程,為什麼呢?

線程的開銷

線程的開銷實際上是非常大的,我們從空間開銷和時間開銷上分別討論。

線程的空間開銷

線程的空間開銷來自這四個部分:

  1. 線程內核對象(Thread Kernel Object)。每個線程都會創建一個這樣的對象,它主要包含線程上下文信息,在32位系統中,它所占用的記憶體在700位元組左右。
  2. 線程環境塊(Thread Environment Block)。TEB包括線程的異常處理鏈,32位系統中占用4KB記憶體。
  3. 用戶模式棧(User Mode Stack),即線程棧。線程棧用於保存方法的參數、局部變數和返回值。每個線程棧占用1024KB的記憶體。要用完這些記憶體很簡單,寫一個不能結束的遞歸方法,讓方法參數和返回值不停地消耗記憶體,很快就會發生 OutOfMemoryException
  4. 內核模式棧(Kernel Mode Stack)。當調用操作系統的內核模式函數時,系統會將函數參數從用戶模式棧複製到內核模式棧。在32位系統中,內核模式棧會占用12KB記憶體。

線程的時間開銷

線程的時間開銷來自這三個過程:

  1. 線程創建的時候,系統相繼初始化以上這些記憶體空間。

  2. 接著CLR會調用所有載入DLL的DLLMain方法,並傳遞連接標誌(線程終止的時候,也會調用DLL的DLLMain方法,並傳遞分離標誌)。

  3. 線程上下文切換。一個系統中會載入很多的進程,而一個進程又包含若幹個線程。但是一個CPU內核在任何時候都只能有一個線程在執行。為了讓每個線程看上去都在運行,系統會不斷地切換“線程上下文”:每個線程及其短暫的執行時間片,然後就會切換到下一個線程了。

    這個線程上下文切換過程大概又分為以下5個步驟:

    • 步驟1進入內核模式。
    • 步驟2將上下文信息(主要是一些CPU寄存器信息)保存到正在執行的線程內核對象上。
    • 步驟3系統獲取一個 Spinlock ,並確定下一個要執行的線程,然後釋放 Spinlock 。如果下一個線程不在同一個進程內,則需要進行虛擬地址交換。
    • 步驟4從將被執行的線程內核對象上傳入上下文信息。
    • 步驟5離開內核模式。

所以,由於要進行如此多的工作,所以創建和銷毀一個線程就意味著代價“昂貴”,即使現在的CPU多核多線程,如無節制的使用線程,依舊會嚴重影響性能。

引入線程池

為了免程式員無節制地使用線程,微軟開發了“線程池”技術。簡單來說,線程池就是替開發人員管理工作線程。當一項工作完畢時,CLR不會銷毀這個線程,而是會保留這個線程一段時間,看是否有別的工作需要這個線程。至於何時銷毀或新起線程,由CLR根據自身的演算法來做這個決定。

線程池技術能讓我們重點關註業務的實現,而不是線程的性能測試。

微軟除實現了線程池外,還需要關註一個類型:BackgroundWorkerBackgroundWorker 是在內部使用了線程池的技術:同時,在WinForm或WPF編碼中,它還給工作線程和UI線程提供了交互的能力。

實際上, ThreadThreadPool 預設都沒有提供這種交互能力,而 BackgroundWorker 卻通過事件提供了這種能力。這種能力包括:報告進度、支持完成回調、取消任務、暫停任務等。

BackgroundWorker 的簡單示例如下:

private BackgroundWorker backgroundWorker = new BackgroundWorker();

private void AsyncButton_Click(object sender, RoutedEventArgs e)
{
    //註冊要執行的任務
    backgroundWorker.DoWork += BackgroundWorker_DoWork;
    //註冊報告進度
    backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
    //註冊完成時的回調
    backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
    //設置允許任務取消
    backgroundWorker.WorkerSupportsCancellation = true;
    //設置允許報告進度
    backgroundWorker.WorkerReportsProgress = true;
    backgroundWorker.RunWorkerAsync();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
    //取消任務
    if (backgroundWorker.IsBusy)
        backgroundWorker.CancelAsync();
}
private void BackgroundWorker_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
{
    //完成時回調
    MessageBox.Show("BackgroundWorker RunWorkerCompleted");
}

private void BackgroundWorker_ProgressChanged(object? sender, ProgressChangedEventArgs e)
{   
    //報告進度
    this.textbox.Text = e.ProgressPercentage.ToString();
}

private void BackgroundWorker_DoWork(object? sender, DoWorkEventArgs e)
{
    BackgroundWorker? worker = sender as BackgroundWorker;

    if (worker != null)
    {
        for (int i = 0; i < 20; i++)
        {
            if (worker.CancellationPending)
            {
                e.Cancel = true;
                break;
            }
            worker.ReportProgress(i);

            Thread.Sleep(100);
        }
    }
}

建議使用WinForm和WPF的開發人員使用 BackgroundWorker

Task替代ThreadPool

ThreadPool 相對於 Thread 來說具有很多優勢,但是 ThreadPool 在使用上卻存在一定的不方便。比如:

  • ThreadPool 不支持線程的取消、完成、失敗通知等交互性操作。
  • ThreadPool 不支持線程執行的先後次序。

所以隨著 Task 類及其所提供的非同步編程模型的引入,Task相較ThreadPool具有更多的優勢。大概有一下幾點:

  1. Task是.NET Framework的一部分,它提供了更高級別的抽象來表示非同步操作或併發任務。相比之下,ThreadPool較為底層,需要手動管理線程池和任務隊列。通過使用Task,我們可以以更簡潔、更可讀的方式表達併發邏輯,而無需關註底層線程管理的細節。

  2. Task是基於Task Parallel Library(TPL)構建的核心組件,它提供了強大的非同步編程支持。利用Task,我們能夠輕鬆定義非同步方法、等待非同步操作完成以及處理任務結果。與此相反,ThreadPool主要用於執行委托或操作,缺乏直接的非同步編程功能。

  3. Task在底層使用ThreadPool來執行任務,但它提供了更優秀的性能和資源管理機制。通過使用Task,我們可以利用TPL提供的任務調度器,智能化地管理線程池的大小、工作竊取演算法和任務優先順序。這樣一來,我們能夠更有效地利用系統資源,並獲得更好的性能表現。

  4. Task擁有強大的任務關聯和組合功能。我們可以使用Task的 ContinueWith()When()WhenAll()Wait()等方法定義任務之間的依賴關係,以及在不同任務完成後執行的操作。這種任務組合方式使併發編程更加靈活且易於管理。

  5. Task提供了更好的異常處理和取消支持機制。我們可以利用Task的異常處理機制捕獲和處理任務中的異常,而不會導致整個應用程式崩潰。此外,Task還引入 CancellationToken 的概念,可用於取消任務的執行,從而更好地控制併發操作。

所以,儘管ThreadPool在某些情況下仍然有其用途,但在C#編程中,使用Task替代ThreadPool已變為通用實踐,推薦優先考慮使用Task來處理併發任務。

參考

編寫高質量代碼:改善C#程式的157個建議 / 陸敏技著.一北京:機械工業出版社,2011.9

作者: Niuery Daily

出處: https://www.cnblogs.com/pandefu/>

郵箱: [email protected]

關於作者:.Net Framework,.Net Core ,WindowsForm,WPF ,控制項庫,多線程

本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出 原文鏈接,否則保留追究法律責任的權利。 如有問題, 可郵件咨詢。


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

-Advertisement-
Play Games
更多相關文章
  • ### [布隆過濾器](https://so.csdn.net/so/search?q=布隆過濾器&spm=1001.2101.3001.7020) - [1、布隆過濾器原理](https://codeleader.blog.csdn.net/article/details/130256000#1_ ...
  • ## 教程簡介 CoffeeScript 是一種相對較新的語言,為開發人員提供了不再有 JavaScript 缺陷的令人期待的方案。利用 CoffeeScript,開發人員即可使用一種輕量級、直觀的語言完成編碼工作,這種語言就像是 Ruby 和 Python 的混合體。對於相容瀏覽器的 Web 應用 ...
  • ## 教程簡介 Excel數據透視表(Excel Pivot Table)是一種互動式的表,可以自由選擇多個欄位的不同組合,用於快速彙總、分析大量數據中欄位與欄位之間的關聯關係。使用數據透視表可以按照數據表格的不同欄位從多個角度進行透視,並建立交叉表格,用以查看數據表格不同層面的彙總信息、分析結果以 ...
  • # 前言 訂單超時自動取消是電商平臺中常見的功能之一,例如在淘寶、京東、拼多多等商城下單後,如果在一定的時間內沒有付款,那麼訂單會自動被取消,是怎麼做到的呢?作為技術人員我們應該瞭解自動取消的原理和實現邏輯,本文將介紹幾種常用的技術方案,幫助開發者實現訂單超時自動取消的功能。 ![](https:/ ...
  • ## 說明 學習數字信號處理演算法時整理的學習筆記。同系列文章目錄可見 [《DSP 學習之路》目錄](https://www.cnblogs.com/young520/p/17539849.html),代碼已上傳到 [Github - ModulationAndDemodulation](https: ...
  • 有時候,我們在處理大量文檔的時候,需要批量給Word文檔添加印章處理,方便列印操作,本篇隨筆介紹利用Aspose.Word對Word文件添加印章處理以及實現業務數據的替換處理。 ...
  • 在編程方面,從來都是實踐出真知,書讀百遍其義自見,所以實戰是最好的提升自己編程能力的方式。 前一段時間,寫了一些實戰系列文章,如: ASP.NET MVC開發學生信息管理系統 Vue+Antdv+Asp.net WebApi開發學生信息管理系統 WPF+Prism+MAH+Asp.net Web A ...
  • 這篇文章主要介紹了介面的概念、定義和實現,以及顯式/隱式實現介面的場景。文章還對介面和抽象類進行了比較,指出它們的區別。同時,文章提供了詳細的代碼示例,方便讀者理解和實踐。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...