C#多線程之線程池篇1

来源:http://www.cnblogs.com/yonghuacui/archive/2016/12/27/6224593.html
-Advertisement-
Play Games

在C#多線程之線程池篇中,我們將學習多線程訪問共用資源的一些通用的技術,我們將學習到以下知識點: 線上程池中調用委托 線上程池中執行非同步操作 線程池和並行度 實現取消選項 使用等待句柄和超時 使用計時器 使用後臺工作組件 在前面的“C#多線程之基礎篇”以及“C#多線程之線程同步篇”中,我們學習瞭如何 ...


  在C#多線程之線程池篇中,我們將學習多線程訪問共用資源的一些通用的技術,我們將學習到以下知識點:

  • 線上程池中調用委托
  • 線上程池中執行非同步操作
  • 線程池和並行度
  • 實現取消選項
  • 使用等待句柄和超時
  • 使用計時器
  • 使用後臺工作組件

  在前面的“C#多線程之基礎篇”以及“C#多線程之線程同步篇”中,我們學習瞭如何創建線程以及如何使用多線程協同工作,在這一篇中,我們將學習另外一種場景,就是我們需要創建許多花費時間非常短的非同步操作來完成某些工作。我們知道創建一個線程是非常昂貴的,因此,對於每個花費時間非常短的非同步操作都創建一個線程是不合適的。

  我們可以使用線程池來解決以上問題,我們可以線上程池中分配一定數量的線程,每當我們需要一個線程時,我們只需要線上程池中取得一個線程即可,而不需要創建一個新的線程,當我們使用完一個線程時,我們僅僅需要把線程重新放入線程池中即可。

  我們可以使用System.Threading.ThreadPool類型來利用線程池。線程池由Common Language Runtime(CLR)進行管理,這意味著每一個CLR只能有一個線程池實例。ThreadPool類型有一個“QueueUserWorkItem”靜態方法,這個靜態方法接收一個委托,該委托代表一個用戶自定義的非同步操作。當這個方法被調用時,這個委托就進入內部隊列,這個時候,如果線程池中沒有線程,則會創建一個新的工作線程,然後將這個委托(第一個)放入隊列中。

  如果先前的操作執行完畢後,我們又放置了一個新的操作到線程池,那麼我們可能會重用上一次操作的那個工作線程。如果我們放置新的操作的時候,線程池中的線程數已達到上限,那麼新的操作會在隊列中等待,直到線程池中有可用工作線程為止。

  需要註意的是,我們儘量線上程池中放置一些需要花費較少時間既能完成的操作,而不要放置需要花費大量時間才能完成的操作,同時不要阻塞工作線程。如果不是這樣,工作線程會變得非常繁忙,以至於不能響應用戶操作,同時也會導致性能問題以及難以調試的錯誤。

  另外,線程池中的工作線程都是後臺線程,這意味著當所有的前臺線程執行完畢後,後臺線程會被停止執行。

  在這一篇中,我們將學習如何使用線程池執行非同步操作、如何取消一個操作以及如何防止長時間運行一個線程。

一、線上程池中調用委托

  在這一小節中,我們將學習如何線上程池中非同步執行一個委托。為了演示如何線上程池中調用一個委托,執行以下操作步驟:

1、使用Visual Studio 2015創建一個新的控制台應用程式。

2、雙擊打開“Program.cs”文件,編寫代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe01
 7 {
 8     class Program
 9     {
10         private delegate string RunOnThreadPool(out int threadId);
11 
12         private static string Test(out int threadId)
13         {
14             WriteLine("Starting...");
15             WriteLine($"Is thread pool thread: {CurrentThread.IsThreadPoolThread}");
16             Sleep(TimeSpan.FromSeconds(2));
17             threadId = CurrentThread.ManagedThreadId;
18             return $"Thread pool worker thread id was : {threadId}";
19         }
20 
21         private static  void Callback(IAsyncResult ar)
22         {
23             WriteLine("Starting a callback...");
24             WriteLine($"State passed to a callback: {ar.AsyncState}");
25             WriteLine($"Is thread pool thread: {CurrentThread.IsThreadPoolThread}");
26             WriteLine($"Thread pool worker thread id: {CurrentThread.ManagedThreadId}");
27         }
28 
29         static void Main(string[] args)
30         {
31             int threadId = 0;
32             var t = new Thread(() => Test(out threadId));
33             t.Start();
34             t.Join();
35             WriteLine($"Thread id: {threadId}");
36 
37             RunOnThreadPool poolDelegate = Test;
38             IAsyncResult r = poolDelegate.BeginInvoke(out threadId, Callback, "a delegate asynchronous call");
39             r.AsyncWaitHandle.WaitOne();
40             string result = poolDelegate.EndInvoke(out threadId, r);
41             WriteLine($"Thread pool worker thread id: {threadId}");
42             WriteLine(result);
43 
44             Sleep(TimeSpan.FromSeconds(2));
45         }
46     }
47 }

3、運行該控制台應用程式,運行效果如下圖所示:

  在第32行代碼處,我們使用老辦法創建了一個線程,然後啟動它,並等待它執行完畢。因為thread的構造方法只接收不帶返回值的委托方法,因此,我們給它傳遞一個lambda表達式,在該表達式中我們調用了“Test”方法。在“Test”方法中,我們使用“Thread.CurrentThread.IsThreadPoolThread”屬性值來判斷線程是不是來自線程池。我們還使用“CurrentThread.ManagedThreadId”屬性值列印出運行當前代碼的線程ID。

  在第10行代碼處,我們定義了一個委托,該委托表示的方法的返回值為字元串類型,並且接收一個整型類型的輸出參數。

  在第37行代碼處,我們將Test方法賦值給poolDelegate委托,併在第38行代碼處,使用委托的“BeginInvoke”方法運行該委托指向的方法(Test)。“BeginInvoke”接收一個回調方法,該方法將在非同步操作完成之後被調用。“BeginInvoke”的第三個參數是傳遞給回調方法的一個用戶自定義的狀態。通常使用這個狀態來分辨一個非同步調用。我們使用“IAsyncResult”介面來保存“BeginInvoke”方法的返回值。

  “BeginInvoke”方法立即返回,這允許我們可以線上程池中的工作線程執行的同時,繼續執行調用“BeginInvoke”方法的線程中的下一條代碼。

  在第40行代碼處,我們可以使用“BeginInvoke”方法的返回值以及對“EndInvoke”方法的調用來獲得非同步操作的結果。

  註意,第39行代碼不是必須的,如果我們註釋掉這一行代碼,程式仍然運行成功,這是因為“EndInvoke”方法會一直等待非同步操作完成。調用“EndInvoke”方法的另一個好處是在工作線程中任何未處理的異常都會拋給調用線程。

  如果我們註釋掉第44行代碼,回調方法“Callback”將不會被執行,這是因為主線程已經結束,所有的後臺線程都會被停止。

二、線上程池中執行非同步操作

  在這一小節中,我們將學習如何線上程池中執行非同步操作,具體步驟如下:

1、使用Visual Studio 2015創建一個新的控制台應用程式。

2、雙擊打開“Program.cs”文件,編寫代碼如下所示:

 1 using System;
 2 using System.Threading;
 3 using static System.Console;
 4 using static System.Threading.Thread;
 5 
 6 namespace Recipe02
 7 {
 8     class Program
 9     {
10         private static void AsyncOperation(object state)
11         {
12             WriteLine($"Operation state: {state ?? "(null)"}");
13             WriteLine($"Worker thread id: {CurrentThread.ManagedThreadId}");
14             Sleep(TimeSpan.FromSeconds(2));
15         }
16 
17         static void Main(string[] args)
18         {
19             const int x = 1;
20             const int y = 2;
21             const string lambdaState = "lambda state 2";
22 
23             ThreadPool.QueueUserWorkItem(AsyncOperation);
24             Sleep(TimeSpan.FromSeconds(2));
25 
26             ThreadPool.QueueUserWorkItem(AsyncOperation, "async state");
27             Sleep(TimeSpan.FromSeconds(2));
28 
29             ThreadPool.QueueUserWorkItem(state =>
30             {
31                 WriteLine($"Operation state: {state}");
32                 WriteLine($"Worker thread id: {CurrentThread.ManagedThreadId}");
33                 Sleep(TimeSpan.FromSeconds(2));
34             }, "lambda state");
35 
36             ThreadPool.QueueUserWorkItem(state =>
37            {
38                WriteLine($"Operation state: {x + y}, {lambdaState}");
39                WriteLine($"Worker thread id: {CurrentThread.ManagedThreadId}");
40                Sleep(TimeSpan.FromSeconds(2));
41            }, "lambda state");
42 
43             Sleep(TimeSpan.FromSeconds(2));
44         }
45     }
46 }

3、運行該控制台應用程式,運行效果(每次運行效果可能不同)如下圖所示:

  在第10~15行代碼處,我們定義了一個帶有object類型參數的“AsyncOperation”方法,然後在第23行代碼處,我們使用ThreadPool的“QueueUserWorkItem”靜態方法線上程池中執行“AsyncOperation”方法。

  在第26行代碼處,我們又一次使用了“QueueUserWorkItem”靜態方法線上程池中執行“AsyncOperation”方法,只不過這次我們給“AsyncOperation”方法傳遞了state參數。

  在第24行和第27行代碼處,我們讓主線程阻塞2秒鐘,以重用線程池中的工作線程。如果我們註釋掉這兩行代碼,那麼工作線程的線程ID大部分情況先將會不一樣。

  在第29~41行代碼中,我們演示瞭如何使用lambda表達式來進行線程池中的非同步操作,請自行分析結果。


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

-Advertisement-
Play Games
更多相關文章
  • Sciter中的Tiscript腳本不是標準的Javascript,是對Javascript的擴展。所以在常用的編輯器和IDE上對於高亮的支持很不好。 不過在Sciter論壇中找到了在VSCode上的高亮支持。安裝方法比較簡單。 首先點此下載插件。 之後在Sciter插件所在目錄執行 然後在VSCo ...
  • 最近修改的一個需求要求捕獲鍵盤輸入的 Text,包括各種標點符合。 最開始想到的是 PreviewKeyDown 或者 PreviewKeyUp 這樣的鍵盤事件。 但是這兩個事件的對象 KeyEventArgs 不夠用,得依靠判斷 Key 來判斷輸入了什麼,然後再根據判斷寫死一個相應鍵位的 Text ...
  • 主要是基於達索軟體Composer Player、的基礎上做些二次開發。 public class ComposerToolBarSetting { public bool AntiAliasingOnIdle { get; set; }//抗鋸齒化 public bool AssySelectio ...
  • ...
  • 最近需要用到多選框,Asp.Net MVC自帶的@Html.ListBox或@Html.ListBoxFor的顯示效果太差,於是找到了Select2進行優化,並正式瞭解了多選框的操作方法。 首先介紹多選框的操作方法; 一、定義一個MultipleSelectModel 二、在Controller中實 ...
  • 這篇文章我們來講講模型綁定(Model Binding),其實在初步瞭解ASP.NET MVC之後,大家可能都會產生一個疑問,為什麼URL片段最後會轉換為例如int型或者其他類型的參數呢?這裡就不得不說模型綁定了。模型綁定是指,用瀏覽器以HTTP請求方式發送的數據來創建.NET對象的過程。每當定義具 ...
  • 初步理解泛型: http://www.cnblogs.com/wilber2013/p/4291435.html 泛型中的類型約束和類型推斷 http://www.cnblogs.com/wilber2013/p/4292240.html 深入理解C#泛型(泛型工作機制) http://www.cn ...
  • 在程式中,使用查找功能是少之不了。今天在ASP.NET環境下演示一回。 在cshtml視圖中,有三個文本框,讓用戶輸入關鍵詞,然後點擊最右連的“搜索”銨鈕,如果有結果將顯示於下麵。 Html: 表格放2行。一行是標題,一行作為輸入框。jQuery代碼: controller: 處理好條件,傳入資料庫 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...