# LOSoMan的Thread、ThreadPool、Task用法與比較講解(C#為主) ###### tags: `LOSo講解` `C#` ## 概念 ### Thread的概念 在[MicroSoft Docs](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread?view=net-5.0)裡介紹到Thread類別可建立和控制執行緒,設定執行緒的優先權,並取得它的狀態。其提供許多屬性及方法,讓編譯者可以對建立好的執行緒(Thread)進行控制並監督其狀態。以下,對於個人覺得常用方法(Method)及屬性(Attribute)作簡單說明: **Thread類別的屬性**: 1. [Name](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread.name?view=net-5.0): 取得執行緒的名稱。 2. [CurrentThread](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread.currentthread?view=net-5.0): 取得目前執行的執行緒。 3. [IsAlive](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread.isalive?view=net-5.0): 回傳Boolean值,若執行緒仍存在為true,不存在為false。 4. [IsBackground](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread.isbackground?view=net-5.0): 取得或設定值,指出執行緒是不是背景執行緒。若設定為true,則程式關閉後執行緒也一併關閉;反之為false,則關閉程式後Thread仍會在背後運行至其自動關閉為止。 **Thread類別的方法**: 1. [Abort( )(已過時)](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread.abort?view=net-5.0): 呼叫這個方法通常會結束執行緒。 2. [Finalize( )](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread.finalize?view=net-5.0): 確認釋出資源,清除Thread所在記憶體。 3. [Sleep(Int32)](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread.sleep?view=net-5.0): 在使用的執行緒內間隔一段時間後繼續執行。 4. [Start( )](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.thread.start?view=net-5.0): 啟用並排程執行緒。 通常,在一個剛開啟的視窗應用程式就已經存在至少兩個Thread,一個為UI的執行緒;另一個為Form的執行緒。當我們要創立新一個執行緒,使用Thread會是很好的選擇。 創立新執行緒的程式示意如下: ```csharp=1 thread = new Thread(DoThread);//指派新的執行緒名稱為thread thread.IsBackground = true;//將其設為背景執行緒 thread.Start();//開始執行緒 ``` "DoThread"方法的程式碼如下: ```csharp=1 private void DoThread() { int count = 0;//初始化計數 while (ThreadControl)//控制執行緒內的while循環 { if (this.InvokeRequired && thread.IsAlive)//跨執行緒委派 { this.BeginInvoke(new MethodInvoker(()=>{ richTextBox1.AppendText(string.Format("Thread count {0}\n", count));//在rtb寫入訊息 richTextBox1.SelectionStart = richTextBox1.TextLength; richTextBox1.ScrollToCaret(); })); } Thread.Sleep(count);//間隔一段時間 count++; } } ``` 此方法牽扯到[跨執行緒委派](https://dotblogs.com.tw/plus/2020/07/14/153827)的行為,因為在子執行緒直接變更UI介面會使程式不穩定以致出錯,所以必須用[InvokeRequired](https://docs.microsoft.com/zh-tw/dotnet/api/system.windows.forms.control.invokerequired?view=net-5.0)去進行判斷,再用Invoke或BeginInvoke方法去執行跨執行緒的程式部分。 ### ThreadPool的概念 [ThreadPool](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.threadpool?view=net-5.0)顧名思義就是放置許多Thread的池(Pool),此類別提供執行緒的集區,可用來執行工作、張貼工作項目、處理非同步 I/O、代表其他執行緒等候,以及處理計時器。ThreadPool類別相較於Thread類別所提供的屬性及方法就少的許多,因此其可控制可觀測性就沒有那麼好。不過,由於ThreadPool是由[CLR](https://docs.microsoft.com/zh-tw/archive/msdn-magazine/2010/september/concurrency-throttling-concurrency-in-the-clr-4-0-threadpool)管控的,所以一個執行緒完成後並不會馬上銷毀,而當ThreadPool要執行新一個執行緒時,直接從ThreadPool提取即可。 (注意: 所有ThreadPool裡的執行緒皆為背景執行緒) 以下,對於個人覺得常用方法(Method)及屬性(Attribute)作簡單說明: **ThreadPool類別的屬性**: 1. [ThreadCount](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.threadpool.threadcount?view=net-5.0): 取得目前存在的執行緒集區執行緒數。 **ThreadPool類別的方法**: 1. [QueueUserWorkItem(WaitCallback)](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.threadpool.queueuserworkitem?view=net-5.0): 將方法排入佇列,以等候執行。 這裡提供一個簡單的ThreadPool使用程式: ```csharp=1 ThreadPool.QueueUserWorkItem(DoThread2) ``` 此為ThreadPool的執行。(將DoThread2方法帶入) "DoThread2"方法的程式碼內容跟"DoThread"方法相同,唯一不同的是多一個傳入型別為object的參數。 ### Task的概念 [Task](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task?view=net-5.0)類別功能相較於前兩者更加強大,其使用的是Pool,但又可以像Thread類別有許多觀測及控制執行緒的方法。Task類別代表不回傳值,而針對傳回值的作業,可以使用[Task< TResult >](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task-1?view=net-5.0)類別。 以下,對於個人覺得常用方法(Method)及屬性(Attribute)作簡單說明: **Task類別的屬性**: 1. [CurrentId](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.currentid?view=net-5.0): 回傳目前執行緒的ID。 2. [Exception](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.exception?view=net-5.0): 取得導致 AggregateException 不當結束的 Task,若正常結束且無例外狀況,則回傳null。 3. [Factory](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.factory?view=net-5.0): 提供 Factory 方法的存取,這些 Factory 方法用於建立及設定 Task 和 Task< TResult > 執行個體。 4. [Id](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.id?view=net-5.0): 取得Task執行緒的ID。 5. [IsCanceled](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.iscanceled?view=net-5.0): 回傳值Boolean,若Task執行緒被取消而完成,則為true;反之為false。 6. [IsCompleted](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.iscompleted?view=net-5.0): 回傳值Boolean,若Task已完成,回傳true;反之為false。 7. [IsFaulted](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.isfaulted?view=net-5.0): 回傳值Boolean,檢查Task是否因錯誤而完成,若是則回傳true;反之為false。 8. [Status](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.status?view=net-5.0): 回傳 TaskStatus 型別的值,此為列舉的所有狀態([Created、Canceled、Faulted、Running...](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.taskstatus?view=net-5.0)) **Task類別的方法**(以下方法幾乎為多載,此以常用為主): 1. [Delay(Int32)](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.delay?view=net-5.0): 在一段時間延遲後才會完成的工作。 2. [Run(Action)](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.run?view=net-5.0): 將指定在ThreadPool執行工作排入佇列,並傳回代表該工作的 Task 物件。 3. [Start( )](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.start?view=net-5.0): 啟動Task。 4. [Wait(Int32)](https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.tasks.task.wait?view=net-5.0): 等待Task完成執行。 以下為幾種Task的常見用法: 用法一,使用Run去執行lambda內的程式。 ```csharp=1 Task.Run(()=> { //Task方法內容... }); ``` 用法二,使用Factory.StartNew去執行lambda內的程式。 ```csharp=1 Task.Factory.StartNew(() => { //Task方法內容... }); ``` 用法三,先建立Task,然後Start。 ```csharp=1 Task t = new Task(() => { //Task方法內容... }); t.Start(); ``` (更多用法可參閱這篇筆記,[請點我](https://kenny88881234.gitlab.io/2020/12/05/Csharp-thread-and-Async/#Task)) ## 差別和比較 由上面Thread、ThreadPool跟Task的概念,我們可以很清楚的知道雖然同樣是建立執行緒或將執行緒排入佇列然後執行,用意感覺差不多,但在用途可就大相逕庭。以下表格可以顯示三者的差異: | | Thread | ThreadPool | Task | | ---------- |:------:|:----------:|:----:| | 可監測 | 中等 | 最低 | 最高 | | 可控制 | 中等 | 最低 | 最高 | | 執行緒數量 | 較少 | 許多 | 皆可 | 通常來講,使用Task會是比較好的選擇。關於Thread跟Task的差異([請點我](https://www.dotnetforall.com/difference-task-and-thread/)) ## 參考 1. [Microsoft C# Document](https://docs.microsoft.com/zh-tw/dotnet/csharp/) 2. [併發- CLR 4.0 ThreadPool 中的併發限制| Microsoft Docs](https://docs.microsoft.com/zh-tw/archive/msdn-magazine/2010/september/concurrency-throttling-concurrency-in-the-clr-4-0-threadpool) 3. [xuplus6493的點部落-[C#] UI跨執行緒](https://dotblogs.com.tw/plus/2020/07/14/153827) 4. [[.NET]Thread vs ThreadPool vs Task](https://dotblogs.com.tw/mileslin/2016/03/11/230450) 5. [C# ThreadPool類(線程池)](https://www.zendei.com/article/56920.html) 6. [Kenny's Blog - C# 多執行緒與非同步](https://kenny88881234.gitlab.io/2020/12/05/Csharp-thread-and-Async/#ThreadPool) 7. [Difference Between C# Task and Thread With Code](https://www.dotnetforall.com/difference-task-and-thread/)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up