.NET 4.0 任務(Task)
隨著 .NET 4.0的到來,她與以前各版本的一個明顯差別就是並行功能的增強,以此來適應這個多核的世界。於是引入了一個新概念—任務,作為支援並行運算的重要組成部分,同時,也作為對執行緒池的一個補充和完善。從所周知,使用執行緒池有兩個明顯的缺點,那就是一旦把我們要執行的任務放進去後,什麼時候執行完成,以及執行完成後需要返回值,我們都無法通過內建的方式而得知。由於任務(Task)的推出,使得我們對並行程式設計變得簡單,而且不用關心底層是怎麼實現的,由於比執行緒池更靈活,如果能掌握好Task,對於寫出高效的並行程式碼非常有幫助。
一、新建任務
在System.Threading.Tasks
名稱空間下,有兩個新類,Task
及其泛型版本Task<TResult>
,這兩個類是用來建立任務的,如果執行的程式碼不需要返回值,請使用Task
,若需要返回值,請使用Task<TResult>
。
建立任務的方式有兩種,一種是通過Task.Factory.StartNew
方法來建立一個新任務,如:
Task task = Task.Facotry.StartNew(()=>Console.WriteLine(“Hello, World!”));//此行程式碼執行後,任務就開始執行
另一種方法是通過Task類的建構函式來建立一個新任務,如:
Task task = new Task(()=>Console.WriteLine(“Hello, World!”));//此處只把要完成的工作交給任務,但任務並未開始
task.Start();//呼叫Start方法後,任務才會在將來某個時候開始執行。
同時,我們可以呼叫Wait
方法來等待任務的完成或者呼叫IsCompleted
屬性來判斷任務是否完成。需要說明的是,兩種建立任務的方法都可以配合TaskCreationOptions
列舉來實現我們對任務執行的行為具體控制, 同時,這兩種建立方式允許我們傳遞一個TaskCreationOptions
物件來取消正在執行中的任務,請看任務的取消。
二、任務的取消
這世界唯一不變的就是變化,當外部條件發生變化時,我們可能會取消正在執行的任務。對於.NET 4.0之前,.NET並未提供一個內建的解決方案來取消執行緒池中正在執行的程式碼,但在.NET 4.0中,我們有了Cooperative Cancellation
模式,這使得取消正在執行的任務變得非常簡單。如下所示:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskDemo
{
class Program
{
static void Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
Task t = new Task(() => LongRunTask(cts.Token));
t.Start();
Thread.Sleep(2000);
cts.Cancel();
Console.Read();
}
static void LongRunTask(CancellationToken token)
{
//此處方法模擬一個耗時的工作
for (int i = 0; i < 1000; i++)
{
if (!token.IsCancellationRequested)
{
Thread.Sleep(500);
Console.Write(".");
}
else
{
Console.WriteLine("任務取消");
break;
}
}
}
}
}
三、任務的異常機制
在任務執行過程中產生的未處理異常,任務會把它暫時隱藏起來,裝進一個集合中。當我們呼叫Wait方法或者Result屬性時,任務會丟擲一個AggregateException
異常。我們可以通過呼叫AggregateException
物件的只讀屬性InnerExceptions
來得到一個ReadOnlyCollection<Exception>
物件,它才是儲存丟擲異常的集合,它的第一個元素就是最初丟擲的異常。同樣的,AggregateException
物件的InnerException
屬性也會返回最初丟擲的異常。
值得重視的是,由於任務的隱藏機制的特點,一旦產生異常後,如果我們不呼叫相應的方法或者屬性檢視異常,我們也無法判斷是否有異常產生(Task
不會主動丟擲異常)。當Task
物件被GC回收時,Finalize
方法會查檢是否有未處理的異常,如果不幸剛才好有,則Finalize
方法會將此AggregateException
再度丟擲,如果再不幸,我們沒有捕獲處理這個異常,則我們的程式會立即中止執行。如果發生這樣的事情,會是多麼大的災難啊!
為了避免這種不幸的發生,我們可以通過註冊TaskScheduler
類的靜態UnobservedTaskException
事件來處理這種未被處理的異常,避免程式的崩潰。
四、任務啟動任務
任務的強大與靈活之一是,當我們完成一個任務時,可以自動開始一個新任務的執行。如下所示:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskDemo
{
public class AutoTask
{
static void Main()
{
Task task = new Task(() => { Thread.Sleep(5000); Console.WriteLine("Hello,"); Thread.Sleep(5000); });
task.Start();
Task newTask = task.ContinueWith(t => Console.WriteLine("World!"));
Console.Read();
}
}
}
對於ContinueWith
方法,我們可以配合TaskContinuationOptions
列舉,得到更多我們想要的行為。
五、子任務
任務是支援父子關係的,即在一個任務中建立新任務。如下所示:
using System;
using System.Threading.Tasks;
namespace TaskDemo
{
class ChildTask
{
static void Main()
{
Task parant = new Task(() =>
{
new Task(() => Console.WriteLine("Hello")).Start();
new Task(() => Console.WriteLine(",")).Start();
new Task(() => Console.WriteLine("World")).Start();
new Task(() => Console.WriteLine("!")).Start();
});
parant.Start();
Console.ReadLine();
}
}
}
值得注意的是,以上程式碼中所示的子任務的呼叫並不是以程式碼的出現先後為順序來呼叫的。
六、任務工廠
在某些情況下,我們會遇到建立大量的任務,而恰好這些任務共用某個狀態引數(如CancellationToken
),為了避免大量的呼叫任務的構造器和一次又一次的引數傳遞,我們可以使用任務工廠來為我們處理這種大量建立工作。如下程式碼所示:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskDemo
{
public class FactoryOfTask
{
static void Main()
{
Task parent = new Task(() =>
{
CancellationTokenSource cts = new CancellationTokenSource();
TaskFactory tf = new TaskFactory(cts.Token);
var childTask = new[]
{
tf.StartNew(()=>ConcreteTask(cts.Token)),
tf.StartNew(()=>ConcreteTask(cts.Token)),
tf.StartNew(()=>ConcreteTask(cts.Token))
};
Thread.Sleep(5000);//此處睡眠等任務開始一定時間後才取消任務
cts.Cancel();
}
);
parent.Start();//開始執行任務
Console.Read();
}
static void ConcreteTask(CancellationToken token)
{
while (true)
{
if (!token.IsCancellationRequested)
{
Thread.Sleep(500);
Console.Write(".");
}
else
{
Console.WriteLine("任務取消");
break;
}
}
}
}
}
七、任務排程程式
任務的排程通過排程程式來實現的,目前,.NET 4.0內建兩種任務排程程式:執行緒池任務排程程式(thread pool task scheduler)和同步上下文任務排程程式(synchronization context task scheduler)。預設情況下,應用程式使用執行緒池任務排程程式呼叫執行緒池的工作執行緒來完成任務,如受計算限制的非同步操作。同步上下文任務排程程式通常使用UI執行緒來完成與Windows Forms
,Windows Presentation Foundation(WPF)
以及SilverLight
應用程式相關的任務。
可喜的是,.NET 4.0 提供了TaskScheduler
抽象類供開發人員繼承來實現自定義任務排程程式的開發,有興趣的同學可以試試。
八、總結
任務給了我們更多的方便性、靈活性的同時,也帶來了比執行緒池更多的資源消耗。如果想減少資源消耗,請直接使用執行緒池QueueUserWorkItem
方法效果會更好;如果想要更多的控制與靈活性,任務(Task)是不二的選擇。這個要我們開發者自己去斟酌了。
相關文章
- Spring - Task定時任務Spring
- .net使用Task多執行緒執行任務 .net限制執行緒數量執行緒
- Flink - Task 任務排程執行流程
- 使用Spring Task輕鬆完成定時任務Spring
- swoole學習手記(四)非同步任務task非同步
- Spring Task定時任務的配置和使用Spring
- CCL 2024 Task7 雙任務冠軍
- Spring Task 定時任務 - 多執行緒配置Spring執行緒
- 宜信開源微服務任務排程平臺(SIA-TASK)微服務
- Gradle的Task(2)任務間的依賴關係Gradle
- 沒有了可用Task slot,Flink新增任務會怎樣?
- 詳解非同步任務 | 看 Serverless Task 如何解決任務排程&可觀測性中的問題非同步Server
- .NET - Task.Run vs Task.Factory.StartNew
- 社群Task挑戰賽開啟,階梯式任務等你來戰
- [原始碼分析] 分散式任務佇列 Celery 之 傳送Task & AMQP原始碼分散式佇列MQ
- .NetCore Hangfire任務計劃NetCore
- .NET定時任務元件Hangfire解析元件
- 溫故之.NET 任務並行並行
- 關機提示 ”task host window任務宿主正在執行關閉任務並且正在停止已執行的任務“我是這樣解決的
- [原創]Swoole和Swoft的那些事(Task投遞/定時任務篇)
- NET作業排程(定時任務)-Quartz.Netquartz
- ASP.NET 網站後臺任務ASP.NET網站
- Java併發程式設計實戰系列6之任務執行(Task Execution)Java程式設計
- [原始碼解析] 並行分散式任務佇列 Celery 之 Task是什麼原始碼並行分散式佇列
- 任務佇列,巨集任務與微任務佇列
- .Net Core實現基於Quart.Net的任務管理
- netcore後臺任務注意事項NetCore
- 【Awen】asp.net定時任務實現ASP.NET
- 巨集任務和微任務
- JavaScript巨集任務和微任務JavaScript
- .Net中非同步任務的取消和監控非同步
- ASP.NET 中的定時執行任務ASP.NET
- 任務
- .Net Core 3.1瀏覽器後端服務(五) 引入定時任務Quartz.Net瀏覽器後端quartz
- Event Loop、 巨集任務和微任務OOP
- JavaScript的巨集任務與微任務JavaScript
- SpringBoot與非同步任務、定時任務、郵件任務Spring Boot非同步
- .NET寶藏API之:IHostedService,後臺任務執行API