<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
ThreadPool是.Net Framework 2.0版本中出現的。
ThreadPool出現的背景:Thread功能繁多,而且對執行緒數量沒有管控,對於執行緒的開闢和銷燬要消耗大量的資源。每次new一個THread都要重新開闢記憶體。
如果某個執行緒的建立和銷燬的代價比較高,同時這個物件還可以反覆使用的,就需要一個池子(容器),儲存多個這樣的物件,需要用的時候從池子裡面獲取,用完之後不用銷燬,在放到池子裡面。這樣不但能節省記憶體資源,提高效能,而且還能管控執行緒的總數量,防止濫用。這時就需要使用ThreadPool了。
我們來看看ThreadPool中常用的一些方法。
QueueUserWorkItem()方法用來啟動一個多執行緒。我們先看看方法的定義:
QueueUserWorkItem()方法有一個WaitCallback型別的引數,在看看WaitCallback的定義:
可以看到WaitCallback就是有一個object型別引數的委託,所以ThreadPool啟動多執行緒使用下面的程式碼:
using System; using System.Threading; namespace ThreadPoolDemo { class Program { static void Main(string[] args) { Console.WriteLine($"start ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); // ThreadPoll啟動多執行緒 ThreadPool.QueueUserWorkItem(p => DoSomethingLong("啟動多執行緒")); Console.WriteLine($"end ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); Console.ReadKey(); } static void DoSomethingLong(string para) { Console.WriteLine($"{para} ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); } } }
執行結果:
GetMaxThreads()用來獲取執行緒池中最多可以有多少個輔助執行緒和最多有多少個非同步執行緒。
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads); Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}");
程式執行結果:
GetMinThreads()用來獲取執行緒池中最少可以有多少個輔助執行緒和最少有多少個非同步執行緒。
ThreadPool.GetMinThreads(out int minworkerThreads, out int mincompletionPortThreads); Console.WriteLine($"GetMinThreads workerThreads={minworkerThreads} completionPortThreads={mincompletionPortThreads}");
程式執行結果:
SetMaxThreads()和SetMinThreads()分別用來設定執行緒池中最多執行緒數和最少執行緒數。
using System; using System.Threading; namespace ThreadPoolDemo { class Program { static void Main(string[] args) { Console.WriteLine($"start ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); // ThreadPoll啟動多執行緒 ThreadPool.QueueUserWorkItem(p => DoSomethingLong("啟動多執行緒")); // 獲取最大執行緒 ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads); Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}"); // 獲取最小執行緒 ThreadPool.GetMinThreads(out int minworkerThreads, out int mincompletionPortThreads); Console.WriteLine($"GetMinThreads workerThreads={minworkerThreads} completionPortThreads={mincompletionPortThreads}"); // 設定執行緒池執行緒 SetThreadPool(); // 輸出設定後的執行緒池執行緒個數 Console.WriteLine("輸出修改後的最多執行緒數和最少執行緒數"); ThreadPool.GetMaxThreads(out int maxworkerThreads, out int maxcompletionPortThreads); Console.WriteLine($"GetMaxThreads workerThreads={maxworkerThreads} completionPortThreads={maxcompletionPortThreads}"); ThreadPool.GetMinThreads(out int workerEditThreads, out int completionPortEditThreads); Console.WriteLine($"GetMinThreads workerThreads={workerEditThreads} completionPortThreads={completionPortEditThreads}"); Console.WriteLine($"end ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); Console.ReadKey(); } static void DoSomethingLong(string para) { Console.WriteLine($"{para} ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); } /// <summary> /// 設定執行緒池執行緒個數 /// </summary> static void SetThreadPool() { Console.WriteLine("************設定最多執行緒數和最少執行緒數****************"); // 設定最大執行緒 ThreadPool.SetMaxThreads(16, 16); // 設定最小執行緒 ThreadPool.SetMinThreads(8, 8); } } }
程式執行結果:
先來看下面一個小例子:
ThreadPool.QueueUserWorkItem(p => DoSomethingLong("啟動多執行緒")); Console.WriteLine("等著QueueUserWorkItem完成後才執行");
我們想讓非同步多執行緒執行完以後再輸出“等著QueueUserWorkItem完成後才執行” 這句話,上面的程式碼執行效果如下:
從截圖中可以看出,效果並不是我們想要的,Thread中提供了暫停、恢復等API,但是ThreadPool中沒有這些API,在ThreadPool中要實現執行緒等待,需要使用到ManualResetEvent類。
ManualResetEvent類的定義如下:
ManualResetEvent需要一個bool型別的引數來表示暫停和停止。上面的程式碼修改如下:
// 引數設定為false ManualResetEvent manualResetEvent = new ManualResetEvent(false); ThreadPool.QueueUserWorkItem(p => { DoSomethingLong("啟動多執行緒"); // 設定為true manualResetEvent.Set(); }); // manualResetEvent.WaitOne(); Console.WriteLine("等著QueueUserWorkItem完成後才執行");
結果:
ManualResetEvent類的引數值執行順序如下:
(1)、false--WaitOne等待--Set--true--WaitOne直接過去
(2)、true--WaitOne直接過去--ReSet--false--WaitOne等待
注意:一般情況下,不要阻塞執行緒池中的執行緒,因為這樣會導致一些無法預見的錯誤。來看下面的一個例子:
static void SetWait() { // 設定最大執行緒 ThreadPool.SetMaxThreads(16, 16); // 設定最小執行緒 ThreadPool.SetMinThreads(8, 8); ManualResetEvent manualResetEvent = new ManualResetEvent(false); for (int i = 0; i < 20; i++) { int k = i; ThreadPool.QueueUserWorkItem(p => { Console.WriteLine(k); if (k < 18) { manualResetEvent.WaitOne(); } else { // 設為true manualResetEvent.Set(); } }); } if (manualResetEvent.WaitOne()) { Console.WriteLine("沒有死鎖、、、"); } else { Console.WriteLine("發生死鎖、、、"); } }
啟動20個執行緒,如果k小於18就阻塞當前的執行緒,結果:
從截圖中看出,只執行了16個執行緒,後面的執行緒沒有執行,這是為什麼呢?因為我們在上面設定了執行緒池中最多可以有16個執行緒,當16個執行緒都阻塞的時候,會造成死鎖,所以後面的執行緒不會再執行了。
ThreadPool可以很好的實現執行緒的重用,這樣就可以減少記憶體的消耗,看下面的程式碼:
/// <summary> /// 測試ThreadPool執行緒重用 /// </summary> static void ThreadPoolTest() { // 執行緒重用 ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); Thread.Sleep(10 * 1000); Console.WriteLine("前面的計算都完成了。。。。。。。。"); ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t =>DoSomethingLong("ThreadPool")); }
然後在Main方法裡面呼叫該方法,輸出結果如下圖所示:
我們在程式碼裡面總共建立了10個執行緒,而結果裡面只有4個執行緒ID,這就說明ThreadPool可以實現執行緒的重用。下面我們在看看Thread是否可以實現執行緒的重用,程式碼如下:
/// <summary> /// 測試Thread執行緒重用 /// </summary> static void ThreadTest() { for (int i = 0; i < 5; i++) { new Thread(() => DoSomethingLong("Threads")).Start(); } Thread.Sleep(10 * 1000); Console.WriteLine("前面的計算都完成了。。。。。。。。"); for (int i = 0; i < 5; i++) { new Thread(() => DoSomethingLong("btnThreads")).Start(); } }
然後在Main方法裡面呼叫,輸入結果如下圖所示:
我們同樣在程式碼裡面建立了10個執行緒,結果輸出了10個執行緒的ID,這就說明Thread不能實現執行緒的重用。同樣也說明THread的效率沒有ThreadPool高。
程式完整程式碼:
using System; using System.Threading; namespace ThreadPoolDemo { class Program { static void Main(string[] args) { Console.WriteLine($"start ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); //// ThreadPoll啟動多執行緒 //ThreadPool.QueueUserWorkItem(p => DoSomethingLong("啟動多執行緒")); //// 獲取最大執行緒 //ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads); //Console.WriteLine($"GetMaxThreads workerThreads={workerThreads} completionPortThreads={completionPortThreads}"); //// 獲取最小執行緒 //ThreadPool.GetMinThreads(out int minworkerThreads, out int mincompletionPortThreads); //Console.WriteLine($"GetMinThreads workerThreads={minworkerThreads} completionPortThreads={mincompletionPortThreads}"); //// 設定執行緒池執行緒 //SetThreadPool(); //// 輸出設定後的執行緒池執行緒個數 //Console.WriteLine("輸出修改後的最多執行緒數和最少執行緒數"); //ThreadPool.GetMaxThreads(out int maxworkerThreads, out int maxcompletionPortThreads); //Console.WriteLine($"GetMaxThreads workerThreads={maxworkerThreads} completionPortThreads={maxcompletionPortThreads}"); //ThreadPool.GetMinThreads(out int workerEditThreads, out int completionPortEditThreads); //Console.WriteLine($"GetMinThreads workerThreads={workerEditThreads} completionPortThreads={completionPortEditThreads}"); //Console.WriteLine($"end ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); //// 引數設定為false //ManualResetEvent manualResetEvent = new ManualResetEvent(false); //ThreadPool.QueueUserWorkItem(p => //{ // DoSomethingLong("啟動多執行緒"); // // 設定為true // manualResetEvent.Set(); //}); //// //manualResetEvent.WaitOne(); //Console.WriteLine("等著QueueUserWorkItem完成後才執行"); // SetWait(); // ThreadPool實現執行緒的重用 // ThreadPoolTest(); // Thread ThreadTest(); Console.WriteLine($"end ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); Console.ReadKey(); } static void DoSomethingLong(string para) { Console.WriteLine($"{para} ThreadId: {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); } /// <summary> /// 設定執行緒池執行緒個數 /// </summary> static void SetThreadPool() { Console.WriteLine("************設定最多執行緒數和最少執行緒數****************"); // 設定最大執行緒 ThreadPool.SetMaxThreads(16, 16); // 設定最小執行緒 ThreadPool.SetMinThreads(8, 8); } static void SetWait() { // 設定最大執行緒 ThreadPool.SetMaxThreads(16, 16); // 設定最小執行緒 ThreadPool.SetMinThreads(8, 8); ManualResetEvent manualResetEvent = new ManualResetEvent(false); for (int i = 0; i < 20; i++) { int k = i; ThreadPool.QueueUserWorkItem(p => { Console.WriteLine(k); if (k < 18) { manualResetEvent.WaitOne(); } else { // 設為true manualResetEvent.Set(); } }); } if (manualResetEvent.WaitOne()) { Console.WriteLine("沒有死鎖、、、"); } else { Console.WriteLine("發生死鎖、、、"); } } /// <summary> /// 測試ThreadPool執行緒重用 /// </summary> static void ThreadPoolTest() { // 執行緒重用 ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); Thread.Sleep(10 * 1000); Console.WriteLine("前面的計算都完成了。。。。。。。。"); ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); ThreadPool.QueueUserWorkItem(t => DoSomethingLong("ThreadPool")); } /// <summary> /// 測試Thread執行緒重用 /// </summary> static void ThreadTest() { for (int i = 0; i < 5; i++) { new Thread(() => DoSomethingLong("Threads")).Start(); } Thread.Sleep(10 * 1000); Console.WriteLine("前面的計算都完成了。。。。。。。。"); for (int i = 0; i < 5; i++) { new Thread(() => DoSomethingLong("btnThreads")).Start(); } } } }
到此這篇關於C#多執行緒之執行緒池ThreadPool用法的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45