C#線程池用法詳細(xì)介紹
介紹
.NET Framework提供了包含ThreadPool類的System.Threading 空間,這是一個(gè)可直接訪問(wèn)的靜態(tài)類,該類對(duì)線程池是必不可少的。它是公共“線程池”設(shè)計(jì)樣式的實(shí)現(xiàn)。對(duì)于后臺(tái)運(yùn)行許多各不相同的任務(wù)是有用的。對(duì)于單個(gè)的后臺(tái)線種而言有更好的選項(xiàng)。
線程的最大數(shù)量。這是完全無(wú)須知道的。在.NET中ThreadPool的所有要點(diǎn)是它自己在內(nèi)部管理線程池中線程。多核機(jī)器將比以往的機(jī)器有更多的線程。微軟如此陳述“線程池通常有一個(gè)線程的最大數(shù)量,如果所有的線程都忙,增加的任務(wù)被放置在隊(duì)列中直到它們能被服務(wù),才能作為可用的線程?!?/P>
用法位置
線程池類型能被用于服務(wù)器和批處理應(yīng)用程序中,線程池有更廉價(jià)的得到線程的內(nèi)部邏輯,因?yàn)楫?dāng)需要時(shí)這些線程已被形成和剛好“連接”,所以線程池風(fēng)格代碼被用在服務(wù)器上。
MSDN表述:“線程池經(jīng)常用在服務(wù)器應(yīng)用程序中,每一個(gè)新進(jìn)來(lái)的需求被分配給一個(gè)線程池中的線程,這樣該需求能被異步的執(zhí)行,沒(méi)有阻礙主線程或推遲后繼需求的處理?!?/P>
MSDN 參考
ThreadPool VS BackgroundWorker
如果你正在使用Windows窗體,寧可使用BackgroundWorker來(lái)對(duì)付那些更簡(jiǎn)單的線程需求,BackgroundWorker在網(wǎng)絡(luò)訪問(wèn)和其他一些簡(jiǎn)單的事情方面做得很好。但對(duì)于多處理器的批處理來(lái)說(shuō),你需要ThreadPool。
BackgroundWorker 教程
當(dāng)你的程序要批處理時(shí),考慮線程池
當(dāng)你的程序產(chǎn)生很多(3個(gè)以上)線程時(shí),考慮線程池
當(dāng)你的程序使用Windows窗體時(shí),考慮后臺(tái)執(zhí)行。
線程要考慮的事 同樣,如何使用線程的細(xì)節(jié)能幫助發(fā)現(xiàn)最好的代碼。下面比較線程情形和哪個(gè)類是最好的。
你需要一個(gè)額外的線程 使用后臺(tái)執(zhí)行
你有許多短期的線程 使用線程池
需求
線程很重要,但對(duì)于那些不會(huì)花很長(zhǎng)時(shí)間來(lái)執(zhí)行且只做一件事情的大多數(shù)應(yīng)用程序來(lái)說(shuō)卻并不重要的。線程對(duì)于界面可用性不是很重要的的應(yīng)用程序而言也不是很重要,要盡量避免使用線程(譯者注:比如進(jìn)度條不是很重要的應(yīng)用程序)。
連接方法
可使用QueueUserWorkItem連接方法(methods)到線程池。方法要運(yùn)行在線程上,則必須把它連接到QueueUserWorkItem。如何實(shí)現(xiàn)呢?必須使用WaitCallback。在MSDN中,WaitCallback被描述成當(dāng)線程池執(zhí)行時(shí)要被調(diào)用的委托回調(diào)方法,是回調(diào)它的參數(shù)的委托。
WaitCallback
只需指定“new WaitCallback”語(yǔ)句作為ThreadPool.QueueUserWorkItem的第一個(gè)參數(shù)來(lái)使用WaitCallback.不需要任何其他的代碼來(lái)使用這方法生效。
使用WaitCallback[c#]的例子
void Example()
{
// 連接 ProcessFile 方法到線程池.
//注意: 'a' 是一個(gè)作為參數(shù)的對(duì)象
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), a);
}
private void ProcessFile(object a)
{
// 我被連接到線程池通過(guò) WaitCallback.
}
參數(shù)
我們能通過(guò)定義一個(gè)特定的類并把一些重要的值放在類里面來(lái)使用參數(shù),那么,方法接收了對(duì)象,就能通過(guò)對(duì)象向方法傳遞多個(gè)參數(shù)了。以下是一個(gè)早期的例子。
使用帶參數(shù)QueueUserWorkItem 的例子[c#]
//指定作為線程池方法的參數(shù)的類
class ThreadInfo
{
public string FileName { get; set; }
public int SelectedIndex { get; set; }
}
class Example
{
public Example()
{
// 聲明一個(gè)新的參數(shù)對(duì)象
ThreadInfo threadInfo = new ThreadInfo();
threadInfo.FileName = "file.txt";
threadInfo.SelectedIndex = 3;
//發(fā)送自定義的對(duì)象到線程方法
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), threadInfo);
}
private void ProcessFile(object a)
{
ThreadInfo threadInfo = a as ThreadInfo;
string fileName = threadInfo.FileName;
int index = thread.SelectedIndex;
}
}
發(fā)生了什么事?我們發(fā)送兩個(gè)值給這個(gè)線程化的ProcessFile方法,它需要知道文件名和選擇索引,而我們?cè)谶@個(gè)對(duì)象中把參數(shù)都發(fā)送了給它。
進(jìn)度條
能通過(guò)從設(shè)計(jì)器中右邊的工具盒面板中增加Windows窗體控件到你的窗體程序來(lái)使用進(jìn)度條并設(shè)置 progressBar1.Value, progressBar1.Minimum 和progressBar1.Maximum。 progressBar1.Value是最小值和最大值中間的位置,以下代碼用來(lái)初始化進(jìn)度條:
設(shè)置進(jìn)度條的例子 [C#]
//設(shè)置進(jìn)度條的長(zhǎng)度.
// 這里我們有6個(gè)單位來(lái)完成,所以6是最大值。
// 最小值通常是0
progressBar1.Maximum = 6; // 或其他數(shù)字
progressBar1.Minimum = 0;
進(jìn)度條位置 你的進(jìn)度條中的有顏色部分是當(dāng)前值與最大值的百分比。所以,如果最大值是6,而值是3即表示做完了一半。
ProgressBar 例子 (Windows Forms)
在進(jìn)度條中調(diào)用Invoke(援引)
讓我們看如何在進(jìn)度條實(shí)例中使用Invoke方法。遺憾的是,你不能在輔助線程中訪問(wèn)Windows控件,因?yàn)閁I線程是分離的,必須使用委托(delegate)和Invoke到進(jìn)度條。
請(qǐng)求Invoke(調(diào)用)的例子[C#]
public partial class MainWindow : Form
{
// 這是運(yùn)行在UI線程來(lái)更新條的委托
public delegate void BarDelegate();
//該窗體的構(gòu)造器(由Visual Studio自己產(chǎn)生)
public MainWindow()
{
InitializeComponent();
}
//當(dāng)按下按鈕,啟動(dòng)一個(gè)新的線程
private void button_Click(object sender, EventArgs e)
{
// 設(shè)定進(jìn)度條的長(zhǎng)度.
progressBar1.Maximum = 6;
progressBar1.Minimum = 0;
// 給線程傳遞這些值.
ThreadInfo threadInfo = new ThreadInfo();
threadInfo.FileName = "file.txt";
threadInfo.SelectedIndex = 3;
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), threadInfo);
}
// 運(yùn)行在后臺(tái)線程上的東西
private void ProcessFile(object a)
{
// (省略)
// 使用'a'做一些重要的事.
// 告訴UI 我們已經(jīng)完成了.
try
{
// 在窗體中調(diào)用委托 .
this.Invoke(new BarDelegate(UpdateBar));
}
catch
{
//當(dāng)一些問(wèn)題發(fā)生后我們能使程序恢復(fù)正常
}
}
//更新進(jìn)度條.
private void UpdateBar()
{
progressBar1.Value++;
if (progressBar1.Value == progressBar1.Maximum)
{
// 結(jié)束了,進(jìn)度條滿了.
}
}
}
委托語(yǔ)法 以上代碼的開(kāi)始處,可以看到聲明 UpdateBar 的委托。它告訴Visual Studio 和C# 需要來(lái)使用這個(gè)作為對(duì)象的方法。
更多需要的工作 以上程序演示了如何設(shè)定進(jìn)度條的最大值和最小值,如何在工作完成后“Invoke”委托方法來(lái)增加進(jìn)度條的大小。
在調(diào)試器中的線程
這兒要顯示如何在Visual Studio的調(diào)試器中查看線程。一旦你有一個(gè)運(yùn)行的程序,你能采取這些措施來(lái)可視化你的線程。首先,以調(diào)試模式打開(kāi)你的線程應(yīng)用程序,一旦你的應(yīng)用程序運(yùn)行在調(diào)試器,告知它去做它的工作而且運(yùn)行這些線程,通過(guò)綠色箭頭運(yùn)行調(diào)試器,當(dāng)線程正在運(yùn)行,在工具條中單擊“pause"按鈕。
下一步 調(diào)試>窗口>線程.該菜單項(xiàng)將打開(kāi)一個(gè)類似下圖的窗口,你能看見(jiàn)有多少線程正在線程池中運(yùn)行。

四個(gè)輔助線程 上圖顯示了共有10個(gè)線程,但只有四個(gè)輔助線程(Worker Thread)在程序中被分配給MainWindow.ProcessFile.
約束輔助線程
如果你有一個(gè)雙核或四核系統(tǒng),你將考慮最多兩個(gè)四個(gè)很費(fèi)力的線程。我們能在運(yùn)行的線程中保持一個(gè)_threadCount 字段并且跟蹤它的數(shù)值。用這個(gè)線程計(jì)數(shù)字段,你將需要在C#語(yǔ)言中使用一個(gè)鎖來(lái)避免造成這個(gè)字段讀和寫的錯(cuò)誤,鎖保護(hù)你的線程被其他線程所改變。
計(jì)數(shù)線程的例子 [C#]
// 鎖住這個(gè)對(duì)象.
readonly object _countLock = new object();
private void ProcessFile(object argument)
{
// 約束輔助線程的數(shù)量
while (true)
{
lock (_countLock)
{
if (_threadCount < 4)
{
// Start the processing
_threadCount++;
break;
}
}
Thread.Sleep(50);
}
// Do work...
}
我們看到什么 以是代碼是異步執(zhí)行的方法。只有其他輔助線程少于4個(gè)時(shí)它才會(huì)工作。這對(duì)于一個(gè)四核機(jī)器是好的。請(qǐng)看描述鎖聲明的更多上下文的文章
Lock Statement
控制線程計(jì)數(shù)器
你可以在ThreadPool上使用SetMinThreads 來(lái)在連發(fā)活動(dòng)中提高吞吐量和性能。以下是關(guān)于可使用的最佳的最小線程數(shù)量的材料。
ThreadPool.SetMinThreads Method
總結(jié)
我們了解了如何在C#程序中使用線程池來(lái)有效管理多個(gè)線程,在Windows 窗體應(yīng)用程序的進(jìn)度條和用戶界面中能給人留很深印象并且也不難實(shí)現(xiàn)。然而,線程帶來(lái)了很多的復(fù)雜性并導(dǎo)致漏洞,線程池是一個(gè)有用的簡(jiǎn)化,但它仍然是困難的。
相關(guān)文章
C#解決“因?yàn)樗惴ú煌蛻舳撕头?wù)器無(wú)法通信”的問(wèn)題
實(shí)現(xiàn)微信退款功能,我們需要在微信支付商戶后臺(tái)申請(qǐng)安全證書(shū),并調(diào)用退款A(yù)PI URL,在調(diào)試過(guò)程中為增添返回調(diào)試信息屬性,調(diào)試一切正常,但再次覆蓋的時(shí)候,調(diào)用顯示為 “ 因?yàn)樗惴ú煌?客戶端和服務(wù)器無(wú)法通信,” ,本文介紹了C#解決因?yàn)樗惴ú煌?客戶端和服務(wù)器無(wú)法通信的問(wèn)題2024-12-12
基于WebRequest.RegisterPrefix的使用詳解
本篇文章對(duì)WebRequest.RegisterPrefix的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C# 獲取當(dāng)前月份天數(shù)的三種方法總結(jié)
本篇文章主要是對(duì)C#中獲取目前月份的天數(shù)的三種方法進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01

