C# 线程池ThreadPool
什么是線(xiàn)程池?為什么要用線(xiàn)程池?怎么用線(xiàn)程池?
1. 什么是線(xiàn)程池?
??? ? ??.NET Framework的ThreadPool類(lèi)提供一個(gè)線(xiàn)程池,該線(xiàn)程池可用于執(zhí)行任務(wù)、發(fā)送工作項(xiàng)、處理異步 I/O、代表其他線(xiàn)程等待以及處理計(jì)時(shí)器。那么什么是線(xiàn)程池?線(xiàn)程池其實(shí)就是一個(gè)存放線(xiàn)程對(duì)象的“池子(pool)”,他提供了一些基本方法,如:設(shè)置pool中最小/最大線(xiàn)程數(shù)量、把要執(zhí)行的方法排入隊(duì)列等等。ThreadPool是一個(gè)靜態(tài)類(lèi),因此可以直接使用,不用創(chuàng)建對(duì)象。
2. 為什么要用線(xiàn)程池?好處是什么?
??????? 微軟官網(wǎng)說(shuō)法如下:許多應(yīng)用程序創(chuàng)建大量處于睡眠狀態(tài),等待事件發(fā)生的線(xiàn)程。還有許多線(xiàn)程可能會(huì)進(jìn)入休眠狀態(tài),這些線(xiàn)程只是為了定期喚醒以輪詢(xún)更改或更新的狀態(tài)信息。?線(xiàn)程池,使您可以通過(guò)由系統(tǒng)管理的工作線(xiàn)程池來(lái)更有效地使用線(xiàn)程。
??????? 說(shuō)得簡(jiǎn)單一點(diǎn),每新建一個(gè)線(xiàn)程都需要占用內(nèi)存空間和其他資源,而新建了那么多線(xiàn)程,有很多在休眠,或者在等待資源釋放;又有許多線(xiàn)程只是周期性的做一些小工作,如刷新數(shù)據(jù)等等,太浪費(fèi)了,劃不來(lái),實(shí)際編程中大量線(xiàn)程突發(fā),然后在短時(shí)間內(nèi)結(jié)束的情況很少見(jiàn)。于是,就提出了線(xiàn)程池的概念。線(xiàn)程池中的線(xiàn)程執(zhí)行完指定的方法后并不會(huì)自動(dòng)消除,而是以?huà)炱馉顟B(tài)返回線(xiàn)程池,如果應(yīng)用程序再次向線(xiàn)程池發(fā)出請(qǐng)求,那么處以?huà)炱馉顟B(tài)的線(xiàn)程就會(huì)被激活并執(zhí)行任務(wù),而不會(huì)創(chuàng)建新線(xiàn)程,這就節(jié)約了很多開(kāi)銷(xiāo)。只有當(dāng)線(xiàn)程數(shù)達(dá)到最大線(xiàn)程數(shù)量,系統(tǒng)才會(huì)自動(dòng)銷(xiāo)毀線(xiàn)程。因此,使用線(xiàn)程池可以避免大量的創(chuàng)建和銷(xiāo)毀的開(kāi)支,具有更好的性能和穩(wěn)定性,其次,開(kāi)發(fā)人員把線(xiàn)程交給系統(tǒng)管理,可以集中精力處理其他任務(wù)。
3. 怎么使用線(xiàn)程池?
其實(shí)線(xiàn)程池使用起來(lái)很簡(jiǎn)單,如下
a.設(shè)置線(xiàn)程池最大最小:
ThreadPool.SetMaxThreads (int workerThreads,int completionPortThreads) 設(shè)置可以同時(shí)處于活動(dòng)狀態(tài)的線(xiàn)程池的請(qǐng)求數(shù)目。所有大于此數(shù)目的請(qǐng)求將保持排隊(duì)狀態(tài),直到線(xiàn)程池線(xiàn)程變?yōu)榭捎?。還可以設(shè)置最小線(xiàn)程數(shù)。
b.將任務(wù)添加進(jìn)線(xiàn)程池:
ThreadPool.QueueUserWorkItem(new?WaitCallback(方法名));
或
ThreadPool.QueueUserWorkItem(new?WaitCallback(方法名), 參數(shù));
舉個(gè)小例子,線(xiàn)程池中最多5個(gè)線(xiàn)程,執(zhí)行一個(gè)方法60次,算5年總工資,如下:
如果不采用線(xiàn)程池,恐怕要開(kāi)60線(xiàn)程異步執(zhí)行Run()方法,空間資源之浪費(fèi),可見(jiàn)一斑。而現(xiàn)在我們最多用了5個(gè)線(xiàn)程,1秒內(nèi)即可執(zhí)行完畢,效率、性能都很好。
----------------------------------------------------------------------------------------------------------------------------------
C#線(xiàn)程池ThreadPool.QueueUserWorkItem接收線(xiàn)程執(zhí)行的方法返回值
最近在項(xiàng)目中需要用到多線(xiàn)程,考慮了一番,選擇了ThreadPool,我的需求是要拿到線(xiàn)程執(zhí)行方法的返回值,
但是ThreadPool.QueueUserWorkItem的回調(diào)方法默認(rèn)是沒(méi)有返回值的,搜了搜,都是簡(jiǎn)單介紹ThreadPool.QueueUserWorkItem的各種
用法,只能自己想辦法了。
回調(diào)方法不帶返回值,迂回一下,回調(diào)方法用對(duì)象的方法,返回值放在對(duì)象的屬性中,在對(duì)象方法執(zhí)行時(shí)將需要的返回值賦值給對(duì)應(yīng)屬性。
等所有線(xiàn)程執(zhí)行完,循環(huán)對(duì)象列表,取回返回值,然后想怎么處理返回值就OK了。上代碼:
封裝對(duì)象:
1 using System; 2 using System.Threading; 3 public class ThreadReturnData 4 { 5 public ManualResetEvent manual; 6 public string res; 7 8 public void ReturnThreadData(object obj) 9 { 10 //線(xiàn)程耗時(shí)操作方法 11 res = DoSomething(obj); 12 manual.Set(); 13 } 14 }多線(xiàn)程調(diào)用:
1 List<ThreadReturnData> testList = new List<ThreadReturnData>(); 2 IList<ManualResetEvent> arrManual = new List<ManualResetEvent>(); 3 for (int i = 0; i < i; i++) 4 { 5 ThreadReturnData temp = new ThreadReturnData(); 6 temp.manual = new ManualResetEvent(false); 7 arrManual.Add(temp.manual); 8 ThreadPool.QueueUserWorkItem(new WaitCallback(temp.ReturnThreadData), i); 9 testList.Add(temp); 10 } 11 } 12 if (arrManual.Count > 0) 13 { 14 ////等待所有線(xiàn)程執(zhí)行完 15 WaitHandle.WaitAll(arrManual.ToArray()); 16 } 17 foreach (ThreadReturnData d in testList) 18 { 19 d.res; 20 //todo 21 }----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
https://blog.csdn.net/zhaoguanghui2012/article/details/52910035
一、CLR線(xiàn)程池
管理線(xiàn)程開(kāi)銷(xiāo)最好的方式:
微軟早就替我們想到了,為我們實(shí)現(xiàn)了線(xiàn)程池。
CLR線(xiàn)程池并不會(huì)在CLR初始化時(shí)立即建立線(xiàn)程,而是在應(yīng)用程序要?jiǎng)?chuàng)建線(xiàn)程來(lái)運(yùn)行任務(wù)時(shí),線(xiàn)程池才初始化一個(gè)線(xiàn)程。
線(xiàn)程池初始化時(shí)是沒(méi)有線(xiàn)程的,線(xiàn)程池里的。線(xiàn)程的初始化與其他線(xiàn)程一樣,但是在完成任務(wù)以后,該線(xiàn)程不會(huì)自行銷(xiāo)毀,而是以?huà)炱鸬臓顟B(tài)返回到線(xiàn)程池。直到應(yīng)用程序再次向線(xiàn)程池發(fā)出請(qǐng)求時(shí),線(xiàn)程池里掛起的線(xiàn)程就會(huì)再度激活執(zhí)行任務(wù)。
這樣既節(jié)省了建立線(xiàn)程所造成的性能損耗,也可以讓多個(gè)任務(wù)反復(fù)重用同一線(xiàn)程,從而在應(yīng)用程序生存期內(nèi)節(jié)約大量開(kāi)銷(xiāo)。
通過(guò)CLR線(xiàn)程池所建立的線(xiàn)程總是默認(rèn)為后臺(tái)線(xiàn)程,優(yōu)先級(jí)數(shù)為T(mén)hreadPriority.Normal。二、工作者線(xiàn)程與I/O線(xiàn)程
CLR線(xiàn)程池分為工作者線(xiàn)程(workerThreads)與I/O線(xiàn)程(completionPortThreads)兩種:
- 工作者線(xiàn)程是主要用作管理CLR內(nèi)部對(duì)象的運(yùn)作,通常用于計(jì)算密集的任務(wù)。
- I/O(Input/Output)線(xiàn)程主要用于與外部系統(tǒng)交互信息,如輸入輸出,CPU僅需在任務(wù)開(kāi)始的時(shí)候,將任務(wù)的參數(shù)傳遞給設(shè)備,然后啟動(dòng)硬件設(shè)備即可。等任務(wù)完成的時(shí)候,CPU收到一個(gè)通知,一般來(lái)說(shuō)是一個(gè)硬件的中斷信號(hào),此時(shí)CPU繼續(xù)后繼的處理工作。在處理過(guò)程中,CPU是不必完全參與處理過(guò)程的,如果正在運(yùn)行的線(xiàn)程不交出CPU的控制權(quán),那么線(xiàn)程也只能處于等待狀態(tài),即使操作系統(tǒng)將當(dāng)前的CPU調(diào)度給其他線(xiàn)程,此時(shí)線(xiàn)程所占用的空間還是被占用,而并沒(méi)有CPU處理這個(gè)線(xiàn)程,可能出現(xiàn)線(xiàn)程資源浪費(fèi)的問(wèn)題。如果這是一個(gè)網(wǎng)絡(luò)服務(wù)程序,每一個(gè)網(wǎng)絡(luò)連接都使用一個(gè)線(xiàn)程管理,可能出現(xiàn)大量線(xiàn)程都在等待網(wǎng)絡(luò)通信,隨著網(wǎng)絡(luò)連接的不斷增加,處于等待狀態(tài)的線(xiàn)程將會(huì)很消耗盡所有的內(nèi)存資源??梢钥紤]使用線(xiàn)程池解決這個(gè)問(wèn)題。
線(xiàn)程池的最大值一般默認(rèn)為1000、2000。當(dāng)大于此數(shù)目的請(qǐng)求時(shí),將保持排隊(duì)狀態(tài),直到線(xiàn)程池里有線(xiàn)程可用。
使用CLR線(xiàn)程池的工作者線(xiàn)程一般有兩種方式:
- 通過(guò)ThreadPool.QueueUserWorkItem()方法;
- 通過(guò)委托;
要注意,不論是通過(guò)ThreadPool.QueueUserWorkItem()還是委托,調(diào)用的都是線(xiàn)程池里的線(xiàn)程。
三、ThreadPool類(lèi)常用方法
通過(guò)以下兩個(gè)方法可以讀取和設(shè)置CLR線(xiàn)程池中工作者線(xiàn)程與I/O線(xiàn)程的最大線(xiàn)程數(shù)。
若想測(cè)試線(xiàn)程池中有多少線(xiàn)程正在投入使用,可以通過(guò)ThreadPool.GetAvailableThreads(out in workThreads,out int conoletionPortThreads)方法。
| 方法 | 說(shuō)明 |
| GetAvailableThreads | 剩余空閑線(xiàn)程數(shù) |
| GetMaxThreads | 最多可用線(xiàn)程數(shù),所有大于此數(shù)目的請(qǐng)求將保持排隊(duì)狀態(tài),直到線(xiàn)程池線(xiàn)程變?yōu)榭捎?/td> |
| GetMinThreads | 檢索線(xiàn)程池在新請(qǐng)求預(yù)測(cè)中維護(hù)的空閑線(xiàn)程數(shù)。 |
| QueueUserWorkItem | 啟動(dòng)線(xiàn)程池里得一個(gè)線(xiàn)程(隊(duì)列的方式,如線(xiàn)程池暫時(shí)沒(méi)空閑線(xiàn)程,則進(jìn)入隊(duì)列排隊(duì)) |
| SetMaxThreads | 設(shè)置線(xiàn)程池中的最大線(xiàn)程數(shù) |
| SetMinThreads | 設(shè)置線(xiàn)程池最少需要保留的線(xiàn)程數(shù) |
四、各種調(diào)用線(xiàn)程池線(xiàn)程的方法
1、通過(guò)QueueUserWorkItem啟動(dòng)工作者線(xiàn)程
ThreadPool線(xiàn)程池中有兩個(gè)重載的靜態(tài)方法可以直接啟動(dòng)工作者線(xiàn)程:
- ThreadPool.QueueUserWorkItem(waitCallback);
- ThreadPool.QueueUserWorkItem(waitCallback,Object);
先把WaitCallback委托指向一個(gè)帶有Object參數(shù)的無(wú)返回值方法,再使用ThreadPool.QueueUserWorkItem(WaitCallback)就可以一步啟動(dòng)此方法,此時(shí)異步方法的參數(shù)被視為null。
下面來(lái)試下用QueueUserWorkItem啟動(dòng)線(xiàn)程池里的一個(gè)線(xiàn)程。注意哦,由于是一直存在于線(xiàn)程池,所以不用new Thread()。
class Program{static void Main(string[] args){//工作者線(xiàn)程最大數(shù)目,I/O線(xiàn)程的最大數(shù)目ThreadPool.SetMaxThreads(1000, 1000); //啟動(dòng)工作者線(xiàn)程ThreadPool.QueueUserWorkItem(new WaitCallback(RunWorkerThread));Console.ReadKey();}static void RunWorkerThread(object state){Console.WriteLine("RunWorkerThread開(kāi)始工作");Console.WriteLine("工作者線(xiàn)程啟動(dòng)成功!");}}輸出:
使用第二個(gè)重載方法ThreadPool.QueueUserWorkItem(WaitCallback,object)方法可以把object對(duì)象作為參數(shù)傳送到回調(diào)函數(shù)中。
class Program{static void Main(string[] args){Person p = new Person(1,"劉備");//啟動(dòng)工作者線(xiàn)程ThreadPool.QueueUserWorkItem(new WaitCallback(RunWorkerThread), p);Console.ReadKey();}static void RunWorkerThread(object obj){Thread.Sleep(200);Console.WriteLine("線(xiàn)程池線(xiàn)程開(kāi)始!");Person p = obj as Person;Console.WriteLine(p.Name);}}public class Person{public Person(int id,string name) { Id = id; Name = name; }public int Id { get; set; }public string Name { get; set; }}輸出結(jié)果如下:
通過(guò)ThreadPool.QueueUserWork啟動(dòng)工作者線(xiàn)程非常方便,但是WaitCallback委托指向的必須是一個(gè)帶有object參數(shù)的無(wú)返回值方法。所以這個(gè)方法啟動(dòng)的工作者線(xiàn)程僅僅適合于帶單個(gè)參數(shù)和無(wú)返回值的情況。
那么如果要傳遞多個(gè)參數(shù)和要有返回值又應(yīng)該怎么辦呢?那就只有通過(guò)委托了。
2、BeginInvoke與EndInvoke委托異步調(diào)用線(xiàn)程
異步調(diào)用委托的步驟如下:
輸出如下:
這種方法有一個(gè)缺點(diǎn),就是不知道異步操作什么時(shí)候執(zhí)行完,什么時(shí)候開(kāi)始調(diào)用EndInvoke,因?yàn)橐坏〦ndInvoke主線(xiàn)程就會(huì)處于阻塞等待狀態(tài)。
3、IAsyncResult輪詢(xún)
為了克服上面提到的缺點(diǎn),此時(shí)可以好好利用IAsyncResult提高主線(xiàn)程的工作性能,IAsyncResult有如下成員。
public interface IAsyncResult {object AsyncState {get;} //獲取用戶(hù)定義的對(duì)象,它限定或包含關(guān)于異步操作的信息。WailHandle AsyncWaitHandle {get;} //獲取用于等待異步操作完成的 WaitHandle。bool CompletedSynchronously {get;} //獲取異步操作是否同步完成的指示。bool IsCompleted {get;} //獲取異步操作是否已完成的指示。 }示例如下:
class Program{delegate string MyDelegate(string name,int age);static void Main(string[] args){MyDelegate myDelegate = new MyDelegate(GetString);IAsyncResult result = myDelegate.BeginInvoke("劉備",22, null, null);Console.WriteLine("主線(xiàn)程繼續(xù)工作!");//比上個(gè)例子,只是利用多了一個(gè)IsCompleted屬性,來(lái)判斷異步線(xiàn)程是否完成 while (!result.IsCompleted){Thread.Sleep(500); Console.WriteLine("異步線(xiàn)程還沒(méi)完成,主線(xiàn)程干其他事!");}string data = myDelegate.EndInvoke(result);Console.WriteLine(data);Console.ReadKey();}static string GetString(string name, int age){Thread.Sleep(2000);return string.Format("我是{0},今年{1}歲!",name,age);}}輸出如下:
以上例子,除了IsCompleted屬性外,還可以使用AsyncWaitHandle如下3個(gè)方法實(shí)現(xiàn)同樣輪詢(xún)判斷效果:
- WaitOne:判斷單個(gè)異步線(xiàn)程是否完成;
- WaitAny:判斷是否異步線(xiàn)程是否有指定數(shù)量個(gè)已完成;
- WaitAll:判斷是否所有的異步線(xiàn)程已完成;
WaitOne:
//比上個(gè)例子,判斷條件由IsCompleted屬性換成了AsyncWaitHandle,僅此而已while (!result.AsyncWaitHandle.WaitOne(200)){Console.WriteLine("異步線(xiàn)程沒(méi)完,主線(xiàn)程繼續(xù)干活!");}WaitAny:
//是否完成了指定數(shù)量WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle };while (WaitHandle.WaitAny(waitHandleList, 200) > 0){Console.WriteLine("異步線(xiàn)程完成數(shù)未大于0,主線(xiàn)程繼續(xù)甘其他事!");}WaitAll:
WaitHandle[] waitHandleList = new WaitHandle[] { result.AsyncWaitHandle };//是否全部異步線(xiàn)程完成while (!WaitHandle.WaitAll(waitHandleList, 200)){Console.WriteLine("異步線(xiàn)程未全部完成,主線(xiàn)程繼續(xù)干其他事!");}4、IAsyncResult回調(diào)函數(shù)
使用輪詢(xún)方式來(lái)檢測(cè)異步方法的狀態(tài)非常麻煩,而且影響了主線(xiàn)程,效率不高。能不能異步線(xiàn)程完成了就直接調(diào)用實(shí)現(xiàn)定義好的處理函數(shù)呢?
有,還是強(qiáng)大的IAsyncResult對(duì)象。
class Program{delegate string MyDelegate(string name, int age);static void Main(string[] args){//建立委托MyDelegate myDelegate = new MyDelegate(GetString);//倒數(shù)第二個(gè)參數(shù),委托中綁定了完成后的回調(diào)方法IAsyncResult result1 = myDelegate.BeginInvoke("劉備",23, new AsyncCallback(Completed), null);//主線(xiàn)程可以繼續(xù)工作而不需要等待Console.WriteLine("我是主線(xiàn)程,我干我的活,不再理你!");Thread.Sleep(5000);//Console.ReadKey(); }static string GetString(string name, int age){Thread.CurrentThread.Name = "異步線(xiàn)程";//注意,如果不設(shè)置為前臺(tái)線(xiàn)程,則主線(xiàn)程完成后就直接卸載程序了//Thread.CurrentThread.IsBackground = false;Thread.Sleep(2000);return string.Format("我是{0},今年{1}歲!", name, age);}//供異步線(xiàn)程完成回調(diào)的方法static void Completed(IAsyncResult result){//獲取委托對(duì)象,調(diào)用EndInvoke方法獲取運(yùn)行結(jié)果AsyncResult _result = (AsyncResult)result;MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate;//獲得參數(shù)string data = myDelegaate.EndInvoke(_result);Console.WriteLine(data);//異步線(xiàn)程執(zhí)行完畢Console.WriteLine("異步線(xiàn)程完成咯!");Console.WriteLine("回調(diào)函數(shù)也是由" + Thread.CurrentThread.Name + "調(diào)用的!");}}輸出如下:
注意:
到目前為止,BeginInvoke("劉備",23, new AsyncCallback(Completed), null)還有最后一個(gè)參數(shù)沒(méi)用過(guò)的。那么最后一個(gè)參數(shù)是用來(lái)干什么?傳參:
namespace 控制臺(tái)___學(xué)習(xí)測(cè)試 {class Program{delegate string MyDelegate(string name, int age);static void Main(string[] args){Person p = new Person(2,"關(guān)羽");//建立委托MyDelegate myDelegate = new MyDelegate(GetString);//最后一個(gè)參數(shù)的作用,原來(lái)是用來(lái)傳參的IAsyncResult result1 = myDelegate.BeginInvoke("劉備", 23, new AsyncCallback(Completed), p);//主線(xiàn)程可以繼續(xù)工作而不需要等待Console.WriteLine("我是主線(xiàn)程,我干我的活,不再理你!");Console.ReadKey();}static string GetString(string name, int age){Thread.CurrentThread.Name = "異步線(xiàn)程";//注意,如果不設(shè)置為前臺(tái)線(xiàn)程,則主線(xiàn)程完成后就直接卸載程序了Thread.CurrentThread.IsBackground = false;Thread.Sleep(2000);return string.Format("我是{0},今年{1}歲!", name, age);}//供異步線(xiàn)程完成回調(diào)的方法static void Completed(IAsyncResult result){//獲取委托對(duì)象,調(diào)用EndInvoke方法獲取運(yùn)行結(jié)果AsyncResult _result = (AsyncResult)result;MyDelegate myDelegaate = (MyDelegate)_result.AsyncDelegate;//獲得參數(shù)string data = myDelegaate.EndInvoke(_result);Console.WriteLine(data);Person p = result.AsyncState as Person;Console.WriteLine("傳過(guò)來(lái)的參數(shù)是:" + p.Name);//異步線(xiàn)程執(zhí)行完畢Console.WriteLine("異步線(xiàn)程完成咯!");Console.WriteLine("回調(diào)函數(shù)也是由" + Thread.CurrentThread.Name + "調(diào)用的!");}}public class Person{public Person(int id, string name){Id = id;Name = name;}public int Id{get;set;}public string Name{get;set;}} }輸出如下:
總結(jié)
以上是生活随笔為你收集整理的C# 线程池ThreadPool的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: yeta机器人_Yeta智能语音电话机器
- 下一篇: OFD文件结构--OFD.xml