在C#編程語言中,使用線程池能夠並行地處理工做,當強制線程和更新進度條時,會使用內建架構的ThreadPool類,爲批處理使用多核結構,這裏咱們來看在C#編程語言中一些關於來自System.Threading的ThreadPool的用法的例子。web
介紹編程
.NET Framework提供了包含ThreadPool類的System.Threading 空間,這是一個可直接訪問的靜態類,該類對線程池是必不可少的。它是公共「線程池」設計樣式的實現。對於後臺運行許多各不相同的任務是有用的。對於單個的後臺線種而言有更好的選項。服務器
線程的最大數量。這是徹底無須知道的。在.NET中ThreadPool的全部要點是它本身在內部管理線程池中線程。多核機器將比以往的機器有更多的線程。微軟如此陳述「線程池一般有一個線程的最大數量,若是全部的線程都忙,增長的任務被放置在隊列中直到它們能被服務,才能做爲可用的線程。」網絡
用法位置架構
線程池類型能被用於服務器和批處理應用程序中,線程池有更廉價的獲得線程的內部邏輯,由於當須要時這些線程已被造成和恰好「鏈接」,因此線程池風格代碼被用在服務器上。異步
MSDN表述:「線程池常常用在服務器應用程序中,每個新進來的需求被分配給一個線程池中的線程,這樣該需求能被異步的執行,沒有阻礙主線程或推遲後繼需求的處理。」編程語言
MSDN 參考工具
ThreadPool VS BackgroundWorker性能
若是你正在使用Windows窗體,寧肯使用BackgroundWorker來對付那些更簡單的線程需求,BackgroundWorker在網絡訪問和其餘一些簡單的事情方面作得很好。但對於多處理器的批處理來講,你須要ThreadPool。this
當你的程序要批處理時,考慮線程池
當你的程序產生不少(3個以上)線程時,考慮線程池
當你的程序使用Windows窗體時,考慮後臺執行。
線程要考慮的事 一樣,如何使用線程的細節能幫助發現最好的代碼。下面比較線程情形和哪一個類是最好的。
你須要一個額外的線程 使用後臺執行
你有許多短時間的線程 使用線程池
需求
線程很重要,但對於那些不會花很長時間來執行且只作一件事情的大多數應用程序來講卻並不重要的。線程對於界面可用性不是很重要的的應用程序而言也不是很重要,要儘可能避免使用線程(譯者注:好比進度條不是很重要的應用程序)。
鏈接方法
可以使用QueueUserWorkItem鏈接方法(methods)到線程池。方法要運行在線程上,則必須把它鏈接到QueueUserWorkItem。如何實現呢?必須使用WaitCallback。在MSDN中,WaitCallback被描述成當線程池執行時要被調用的委託回調方法,是回調它的參數的委託。
WaitCallback
只需指定「new WaitCallback」語句做爲ThreadPool.QueueUserWorkItem的第一個參數來使用WaitCallback.不須要任何其餘的代碼來使用這方法生效。
使用WaitCallback的例子
void Example() { // 鏈接 ProcessFile 方法到線程池. //注意: 'a' 是一個做爲參數的對象 ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), a); } private void ProcessFile(object a) { // 我被鏈接到線程池經過 WaitCallback. }
參數
咱們能經過定義一個特定的類並把一些重要的值放在類裏面來使用參數,那麼,方法接收了對象,就能經過對象向方法傳遞多個參數了。如下是一個早期的例子。
使用帶參數QueueUserWorkItem 的例子
//指定做爲線程池方法的參數的類 class ThreadInfo { public string FileName { get; set; } public int SelectedIndex { get; set; } } class Example { public Example() { // 聲明一個新的參數對象 ThreadInfo threadInfo = new ThreadInfo(); threadInfo.FileName = "file.txt"; threadInfo.SelectedIndex = 3; //發送自定義的對象到線程方法 ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), threadInfo); } private void ProcessFile(object a) { ThreadInfo threadInfo = a as ThreadInfo; string fileName = threadInfo.FileName; int index = thread.SelectedIndex; } }
發生了什麼事?咱們發送兩個值給這個線程化的ProcessFile方法,它須要知道文件名和選擇索引,而咱們在這個對象中把參數都發送了給它。
進度條
能經過從設計器中右邊的工具盒面板中增長Windows窗體控件到你的窗體程序來使用進度條並設置 progressBar1.Value, progressBar1.Minimum 和progressBar1.Maximum。 progressBar1.Value是最小值和最大值中間的位置,如下代碼用來初始化進度條:
設置進度條的例子
//設置進度條的長度. // 這裏咱們有6個單位來完成,因此6是最大值。 // 最小值一般是0 progressBar1.Maximum = 6; // 或其餘數字 progressBar1.Minimum = 0;
進度條位置 你的進度條中的有顏色部分是當前值與最大值的百分比。因此,若是最大值是6,而值是3即表示作完了一半。
ProgressBar 例子 (Windows Forms)
在進度條中調用Invoke(援引)
讓咱們看如何在進度條實例中使用Invoke方法。遺憾的是,你不能在輔助線程中訪問Windows控件,由於UI線程是分離的,必須使用委託(delegate)和Invoke到進度條。
請求Invoke(調用)的例子
public partial class MainWindow : Form { // 這是運行在UI線程來更新條的委託 public delegate void BarDelegate(); //該窗體的構造器(由Visual Studio本身產生) public MainWindow() { InitializeComponent(); } //當按下按鈕,啓動一個新的線程 private void button_Click(object sender, EventArgs e) { // 設定進度條的長度. progressBar1.Maximum = 6; progressBar1.Minimum = 0; // 給線程傳遞這些值. ThreadInfo threadInfo = new ThreadInfo(); threadInfo.FileName = "file.txt"; threadInfo.SelectedIndex = 3; ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), threadInfo); } // 運行在後臺線程上的東西 private void ProcessFile(object a) { // (省略) // 使用'a'作一些重要的事. // 告訴UI 咱們已經完成了. try { // 在窗體中調用委託 . this.Invoke(new BarDelegate(UpdateBar)); } catch { //當一些問題發生後咱們能使程序恢復正常 } } //更新進度條. private void UpdateBar() { progressBar1.Value++; if (progressBar1.Value == progressBar1.Maximum) { // 結束了,進度條滿了. } } }
委託語法 以上代碼的開始處,能夠看到聲明 UpdateBar 的委託。它告訴Visual Studio 和C# 須要來使用這個做爲對象的方法。
更多須要的工做 以上程序演示瞭如何設定進度條的最大值和最小值,如何在工做完成後「Invoke」委託方法來增長進度條的大小。
在調試器中的線程
這兒要顯示如何在Visual Studio的調試器中查看線程。一旦你有一個運行的程序,你能採起這些措施來可視化你的線程。首先,以調試模式打開你的線程應用程序,一旦你的應用程序運行在調試器,告知它去作它的工做並且運行這些線程,經過綠色箭頭運行調試器,當線程正在運行,在工具條中單擊「pause"按鈕。
下一步 調試>窗口>線程.該菜單項將打開一個相似下圖的窗口,你能看見有多少線程正在線程池中運行。
四個輔助線程 上圖顯示了共有10個線程,但只有四個輔助線程(Worker Thread)在程序中被分配給MainWindow.ProcessFile.
約束輔助線程
若是你有一個雙核或四核系統,你將考慮最多兩個四個很費力的線程。咱們能在運行的線程中保持一個_threadCount 字段而且跟蹤它的數值。用這個線程計數字段,你將須要在C#語言中使用一個鎖來避免形成這個字段讀和寫的錯誤,鎖保護你的線程被其餘線程所改變。
計數線程的例子
// 鎖住這個對象. readonly object _countLock = new object(); private void ProcessFile(object argument) { // 約束輔助線程的數量 while (true) { lock (_countLock) { if (_threadCount < 4) { // Start the processing _threadCount++; break; } } Thread.Sleep(50); } // Do work... }
咱們看到什麼 以是代碼是異步執行的方法。只有其餘輔助線程少於4個時它纔會工做。這對於一個四核機器是好的。請看描述鎖聲明的更多上下文的文章
控制線程計數器
你能夠在ThreadPool上使用SetMinThreads 來在連發活動中提升吞吐量和性能。如下是關於可以使用的最佳的最小線程數量的材料。
ThreadPool.SetMinThreads Method
總結
咱們瞭解瞭如何在C#程序中使用線程池來有效管理多個線程,在Windows 窗體應用程序的進度條和用戶界面中能給人留很深印象而且也不難實現。然而,線程帶來了不少的複雜性並致使漏洞,線程池是一個有用的簡化,但它仍然是困難的。