多執行緒詳細介紹
什麼是程序執行緒:我們來看一下自己的工作管理員
這裡的每一項都是一個程序,我們的釋出的每一個應用程式都需要一個程序去執行,在一個程序內可以有多個執行緒去計算執行程式。我們看下面的圖片:
我們可以看一下程序和執行緒的數量,很明顯可以看出,執行緒和程序的關係。我們的每一個操作都需要一個執行緒來執行,滑鼠的點選就需要執行緒去響應我們的操作。
現在我們不難理解,我們一個應用程式就代表一個程序,想讓我們的程式高效的執行我們就可以啟用多個執行緒去執行了,當然採用多執行緒的話有好處也是有代價的,好處合理的利用計資源了,但是執行緒過多了,你的CPU利用率就加大了,也有可能導致電腦的卡死。
在我們的程式中執行緒的代表就是:Thread本篇文章我們就說一下執行緒。我們先了解一下同步非同步。
同步:指的是在同一執行緒下執行,並且會等待結果執行完畢。
非同步:不再同一個執行緒下執行,並且執行得順序不可控,不會等待執行結果完畢。
我們先用委託來演示一下多執行緒,如果不怎麼了解委託得可以看一下上一篇文章:
DelegateMethod Method = () => { Console.WriteLine($"我的執行緒ID是:{Thread.CurrentThread.ManagedThreadId}"); }; Method.BeginInvoke(null, null); Method.Invoke();
本來想用上面的程式碼去先簡單的演示一下,誰知道NetCore 的程式集提供了,但是平臺目前還不支援。 我們可以私下使用NET 試一下。
使用Thread 演示
Console.WriteLine($"*********************************我是同步方法 *******************************"); for (int i = 0; i < 10; i++) { Console.WriteLine($"我的執行緒ID是:{Thread.CurrentThread.ManagedThreadId}"); } Console.WriteLine($"*********************************同步方法結束 *******************************"); Console.WriteLine($"*********************************我是非同步方法 *******************************"); for (int i = 0; i < 10; i++) { Thread thread = new Thread(() => { Console.WriteLine($"我的執行緒ID是:{Thread.CurrentThread.ManagedThreadId}"); }); thread.Start(); } Console.WriteLine($"*********************************非同步方法結束 *******************************"); Console.ReadKey();
執行結果:
上面的程式碼執行結果中我們可以看到:同步方法 是同一個執行緒來執行的,之上而下有序的執行,但是非同步方法啟動了多個執行緒去執行的,並且執行緒是無序的。看到這樣的情況我們就會知道如果我啟動了很多執行緒執行緒用完之後也是有回收的,回收之後同樣會分配的,也就是說同一個操作中執行緒的ID 可能會多次出現的。
Console.WriteLine($"*********************************非同步啟動執行緒數量計算*******************************"); List<int> ListInt = new List<int>(); int Conut = 0; for (int i = 0; i < 2000; i++) { Thread thread = new Thread(() => { if (ListInt.Contains(Thread.CurrentThread.ManagedThreadId)) { Conut++; Console.WriteLine($"我的重複的執行緒我的 ID是:{Thread.CurrentThread.ManagedThreadId}重複執行緒總數量:{Conut}"); } ListInt.Add(Thread.CurrentThread.ManagedThreadId); }); thread.Start(); } Console.WriteLine($"*********************************非同步啟動執行緒數量計算結果:{ListInt.Count}*******************************");
結果:
上面的結果我們可以看到:執行緒回收再利用。其實thread執行緒的回收就是我們的GC來做的,這就是C# 的強大之處,自動幫助我們回收了。需要注意的是這樣使用執行緒我們的回收過程是比較慢的,這個回收速度是我們計算機效能決定的。
在上面的結果中我們可以看到我們總共申請了1996個執行緒,其中有881個執行緒是重複的,執行緒的申請和銷燬是耗費很多效能的,接下來我們看一下執行緒池。
執行緒thread有哪些可操作的屬性
CurrentContext |
獲取執行緒正在其中執行的當前上下文。 |
CurrentCulture |
獲取或設定當前執行緒的區域性。 |
CurrentPrinciple |
獲取或設定執行緒的當前負責人(對基於角色的安全性而言)。 |
CurrentThread |
獲取當前正在執行的執行緒。 |
CurrentUICulture |
獲取或設定資源管理器使用的當前區域性以便在執行時查詢區域性特定的資源。 |
ExecutionContext |
獲取一個 ExecutionContext 物件,該物件包含有關當前執行緒的各種上下文的資訊。 |
IsAlive |
獲取一個值,該值指示當前執行緒的執行狀態。 |
IsBackground |
獲取或設定一個值,該值指示某個執行緒是否為後臺執行緒。 |
IsThreadPoolThread |
獲取一個值,該值指示執行緒是否屬於託管執行緒池。 |
ManagedThreadId |
獲取當前託管執行緒的唯一識別符號。 |
Name |
獲取或設定執行緒的名稱。 |
Priority |
獲取或設定一個值,該值指示執行緒的排程優先順序。 |
ThreadState |
獲取一個值,該值包含當前執行緒的狀態。 |
執行緒池: ThreadPool
ThreadPool:執行緒池中的執行緒都是後臺執行緒IsBackground屬性都是True.不會影響所有的前臺執行緒,也就是說不會影響使用者體驗。每個執行緒都有預設的堆疊大小和優先順序,位於多執行緒池中。 一旦執行緒池中的執行緒完成任務,它將返回到等待執行緒佇列中,這時我們就可以利用這些閒置的執行緒。通過這種重複使用,應用程式可以避免產生為每個任務建立新執行緒的開銷。在每一個程序中都只會有一個執行緒池
//public static bool SetMaxThreads(int workerThreads, int completionPortThreads); //public static bool SetMinThreads(int workerThreads, int completionPortThreads); //workerThreads 工作執行緒數量completionPortThreads I/O執行緒數量 ThreadPool.SetMaxThreads(12,12); ThreadPool.SetMinThreads(12, 12);
我這裡設定工作執行緒,I/O執行緒數量來源於我得計算機核心數量,保持每個核心最大最小執行緒都啟動一個。檢視計算機處理器核心數量。
上面的設定是說我線上程池中給準備了數量為12 的執行緒。你可以申請最多12個執行緒,在使用完之後我會立馬進行自動的回收,回收之後的執行緒繼續存放線上程池中等待使用。相比於 Thread執行緒池ThreadPool對於執行緒的回收更快,效能更好。
程式碼看一下效能:
Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < 1000; i++) { Thread thread = new Thread(() => { Console.WriteLine($"*********************************我是多執行緒Thread方法 *******************************"); }); thread.Start(); } Console.WriteLine($"*********************************Thread方法結束耗費時間 :{stopwatch.ElapsedMilliseconds}*******************************"); Console.WriteLine($"*********************************多執行緒ThreadPool啟動*******************************"); Stopwatch stopwatch1 = new Stopwatch(); stopwatch1.Start(); for (int i = 0; i < 1000; i++) { WaitCallback act = (t) => { Console.WriteLine($"*********************************我是多執行緒ThreadPool方法 *******************************"); }; ThreadPool.QueueUserWorkItem(act); } Console.WriteLine($"*********************************ThreadPool方法結束耗費時間 :{stopwatch1.ElapsedMilliseconds}*******************************");
結果: Thread
結果: ThreadPool
上面的兩個方法我們都只是輸出一行字元,但是以1000次的來說看一下效能相差有多少。所以建議大家都使用執行緒池。
推薦官方文件:https://docs.microsoft.com/zh-cn/dotnet/standard/threading/?view=netframework-4.7.2
執行緒池:Task
隨著框架的發展我們有了Task 他也是基於ThreadPool來重新封裝的。他的出現方便了我們對多執行緒的 回撥等待 更好的操作。
推薦一篇部落格:https://www.cnblogs.com/lonelyxmas/p/9509298.html
平行計算的多執行緒 Parallel
Parallel 多執行緒,這個類似同步的,他是在Task的基礎之上又一次的封裝。假如說我們啟動多個執行緒,她像其他的一樣啟動了很多的子執行緒去執行的,而是和當前執行緒一樣並行去執行的。並且當前執行緒也參與執行。他會卡住執行緒,等到全部執行完畢後才會繼續
這個操作上不如Task 靈活,比如Task 可以等待其中一個執行緒執行完成後繼續主執行緒,Parallel 是必須等待全部執行完畢。
Parallel裡面大致分為三個方法: For,ForEach,Invoke
Invoke:
Console.WriteLine($"*********************************我是主執行緒執行緒 ID:{Thread.CurrentThread.ManagedThreadId} *******************************"); Action act = () => { Console.WriteLine($"我的執行緒ID是:{Thread.CurrentThread.ManagedThreadId}"); }; Parallel.Invoke(act, act, act, act, act);
結果:
上面結果中我們可以看到主執行緒參與了進來。
ForEach:
List<int> vs = new List<int>() { 1, 2, 3, 4, 5 }; Parallel.ForEach<int>(vs, t => { Console.WriteLine($"*********************************我是{t} *******************************"); });
結果:
For
List<int> vs = new List<int>() { 1, 2, 3, 4, 5 }; //從零開始 迴圈多少次 Parallel.For(0, vs.Count,t=> { Console.WriteLine($"*********************************我是{t} *******************************"); });
結果:
上面的程式碼中我們可以看到Parallel 適合在我們迴圈的時候去使用這樣並行的去執行,我們可以減少程式的執行時間。
如果當我們需要執行的集合過大有可能會 並行很多執行緒時我們怕會影響我們計算機的I/O 我們還可以設定最大的並行數防止程式執行時i/o風暴
//設定 Parallel 最大並行執行緒的數量 ParallelOptions options = new ParallelOptions(); //最大並行數為10; options.MaxDegreeOfParallelism = 10;
Parallel 官方介紹:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.parallel?redirectedfrom=MSDN&view=netframework-4.7.2
獲取當前計算機 最大執行緒數
int workerThreads; int completionPortThreads; ThreadPool.GetMaxThreads( out workerThreads, out completionPortThreads); Console.WriteLine($"最大工作執行緒:{workerThreads} 最大I/O執行緒:{completionPortThreads} "); ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads); Console.WriteLine($"最小工作執行緒:{workerThreads} 最小 I/O執行緒:{completionPortThreads} ");
結果:
有不足之處 希望大家指出相互學習,
轉載請註明出處 謝謝!