您好,登錄后才能下訂單哦!
本篇文章為大家展示了分享一些常見c#中線程的面試題,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
線程的知識太多,知識點有深有淺,往深的研究會涉及操作系統、CPU、內存,往淺了說就是一些語法。沒有一定的知識積累,很難把線程的知識寫得全面,當然我也沒有這個能力。所以想到一個點寫一個點,盡量總結一些有用的知識點。線程是個大話題,這個系列可能會有好幾遍關于線程的,先從基礎的開始,熱熱身。
一些基礎概念
線程(Thread)是操作系統能夠進行運算調度的最小單位。它是進程中的實際運作單位,一個進程中可以啟動多個線程,每個線程可以并行執行不同的任務。嚴格意義上來說,同一時間可以并行運行的線程數取決于 CPU 的核數。
根據線程運行模式,可以把線程分為前臺線程、后臺線程和守護(Daemon)線程:
每次開啟一個新的線程都要消耗一定的內存,即使線程什么也不做,也會至少消耗 1M 左右的內存。
多線程并行(Parallelism)和并發(Concurrency)的區別:
PS:以上概念來源 Google 的多個搜索結果,稍加整理。
Thread、ThreadPool 和 Task
對 C# 開發者來說,不可不理解清楚 Thread、ThreadPool 和 Task 這三個概念。這也是面試頻率很高的話題,在 StackOverflow 可以找到有很多不錯的回答,我總結整理了一下。
Thread
Thread 是一個實際的操作系統級別的線程(OS 線程),有自己的棧和內核資源。Thread 允許最高程度的控制,你可以 Abort、Suspend 或 Resume 一個線程,你還可以監聽它的狀態,設置它的堆棧大小和 Culture 等屬性。Thread 的開銷成本很高,你的每一個線程都會為它的堆棧消耗相對較多的內存,并且在線程之間的處理器上下文切換時會增加額外的 CPU 開銷。
ThreadPool
ThreadPool(線程池)是一堆線程的包裝器,由 CLR 維護。你對線程池中的線程沒有任何控制權,你甚至無法知道線程池什么時候開始執行你提交的任務,你只能控制線程池的大小。簡單來說,線程池調用線程的機制是,它首先調用已創建的空閑線程來執行你的任務,如果當前沒有空閑線程,可能會創建新線程,也可能會等待。
使用 ThreadPool 可以避免創建太多線程的開銷。但是,如果你向 ThreadPool 提交了太多長時間運行的任務,它可能會被填滿,這時你提交的后面的任務可能最終會等待前面的長時間運行的任務執行完成。此外,線程池沒有提供任何方法來檢測一個工作任務何時完成(不像 Thread.Join()),也沒有方法來獲取結果。因此,ThreadPool 最好用于調用者不需要結果的短時操作。
Task
Task 是 TPL(Task Parallel Library)提供一個類,它在 Thread 和 TheadPool 之間提供了兩全其美的解決方案。和 ThreadPool 一樣,Task 并不創建自己的OS 線程。相反,Task 是由 TaskScheduler 調度器執行的,默認的調度器只是在 ThreadPool 上運行。
與 ThreadPool 不同的是,Task 還允許你知道它完成的時間,并獲取返回一個結果。你可以在現有的 Task 上調用 ContinueWith(),使它在任務完成后運行更多的代碼(如果它已經完成,就會立即運行回調)。
你也可以通過調用 Wait() 來同步等待一個任務的完成(或者,通過獲取它的 Result 屬性)。與 Thread.Join() 一樣,這將阻塞調用線程,直到任務完成。通常不建議同步等待任務執行完成,它使調用線程無法進行任何其他工作。如果當前線程要等待其它線程任務執行完成,建議使用 async/await 異步等待,這樣當前線程可以空閑出來去處理其它任務,比如在 await Task.Delay() 時,并不占用線程資源。
由于任務仍然在 ThreadPool 上運行,因此不應該將其用于長時任務的執行,因為它們會填滿線程池并阻塞新的工作任務。相反,Task 提供了一個 LongRunning 選項,它將告訴 TaskScheduler 啟用一個新的線程,而不是在 ThreadPool 上運行。
所有較新的上層多線程 API,包括 Parallel.ForEach()、PLINQ、async/await 等,都是建立在 Task 上的。
Thread 和 Task 簡單示例
下面通過一個簡單示例演示 Thread 和 Task 的使用,注意他們是如何創建、傳參、執行和等待執行完成的。
static void Main(string[] args) { // 創建兩個新的 Thread var thread1 = new Thread(new ThreadStart(() => PerformAction("Thread", 1))); var thread2 = new Thread(new ThreadStart(() => PerformAction("Thread", 2))); // 開始執行線程任務 thread1.Start(); thread2.Start(); // 等待兩個線程執行完成 thread1.Join(); thread1.Join(); Console.WriteLine("Theads done!"); Console.WriteLine("===我是分隔線==="); // 創建兩個新的 Task var task1 = Task.Run(() => PerformAction("Task", 1)); var task2 = Task.Run(() => PerformAction("Task", 2)); // 執行并等待兩個 Task 執行完成 Task.WaitAll(new[] { task1, task2 }); Console.WriteLine("Tasks done!"); Console.ReadKey(); } static void PerformAction(string threadOrTask, int id) { var rnd = new Random(id); for (int i = 0; i < 5; i++) { Console.WriteLine($"{threadOrTask}: {id}: {i}", id, i); Thread.Sleep(rnd.Next(0, 1000)); } }
運行效果:
注意到,相比之下 Task 比 Thread 好用得多,加上前文 Task 和 Thread 的對比,對我們編碼的指導意義是:大多數情況我們應該使用 Task,而不要直接使用 Thread,除非你明確知道你需要一個獨立的線程來執行一個長耗時的任務。
上述內容就是分享一些常見c#中線程的面試題,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。