Task是.NET4.0加入的,跟線程池ThreadPool的功能類似,用Task開啟新任務時,會從線程池中調用線程,而Thread每次實例化都會創建一個新的線程。
我們可以說Task是一種基於任務的編程模型。它與thread的主要區別是,它更加方便對線程進程調度和獲取線程的執行結果。
Task類和Task<TResult>類
前者接收的是Action委托類型
后者接收的是Func<TResult>委托類型
任務和線程的區別:
1、任務是架構在線程之上的,也就是說任務最終還是要拋給線程去執行。
2、任務跟線程不是一對一的關系,比如開10個任務並不是說會開10個線程,這一點任務有點類似線程池,但是任務相比
線程池有很小的開銷和精確的控制。
一、Task的創建
1、直接創建
var task1 = new Task(() => { Console.WriteLine("Begin"); System.Threading.Thread.Sleep(5000); Console.WriteLine("Finish"); }); Console.WriteLine("Before start:" + task1.Status); task1.Start();
2、工廠創建
Task.Factory.StartNew(()={
});
3、4.5以后Run運行
Task.Run(()=>{
});
4、一種方便獲取返回值的方式
static void Main(string[] args) { var tcs = new TaskCompletionSource<int>(); new Thread(() => { Thread.Sleep(5000); int i = Enumerable.Range(1, 100).Sum(); tcs.SetResult(i); }).Start();//線程把運行計算結果,設為tcs的Result。 Task<int> task = tcs.Task; Console.WriteLine(task.Result); //此處會阻塞,直到匿名線程調用tcs.SetResult(i)完畢 }
二、細節解釋
看下面代碼:
namespace WpfApplication6 { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); ConsoleManager.Show();//打開控制台窗口 } private void Window_Loaded(object sender, RoutedEventArgs e) { Console.WriteLine("主線程啟動"); Task task = Task.Run(() => { Thread.Sleep(1500); Console.WriteLine("task啟動"); }); Thread.Sleep(300); task.Wait(); Console.WriteLine("主線程結束"); } } }
結果:
分析:
開啟新任務的方法:Task.Run()或者Task.Factory.StartNew(),開啟的是后台線程
要在主線程中等待后台線程執行完畢,可以使用Wait方法(會以同步的方式來執行)。不用Wait則會以異步的方式來執行。
thread和Task的區別,thread new多少個就會創建多少個線程,而task是利用線程池中的線程。
task<TResult>就是有返回值的Task,TResult就是返回值類型。示例:
private void Window_Loaded(object sender, RoutedEventArgs e) { Console.WriteLine("主線程開始"); //返回值類型為string Task<string> task = Task<string>.Run(() => { Thread.Sleep(2000); return Thread.CurrentThread.ManagedThreadId.ToString(); }); //會等到task執行完畢才會輸出; Console.WriteLine(task.Result); Console.WriteLine("主線程結束"); }
通過task.Result可以取到返回值,若取值的時候,后台線程還沒執行完,則會等待其執行完畢!
簡單提一下:
Task任務可以通過CancellationTokenSource類來取消。
三、Task的其他方法
Task.Wait(); //阻塞當前線程 Task.WaitAll(); //阻塞當前線程直到所有的任務執行完畢 Task.WaitAny(); //阻塞當前線程直到有任意一個任務執行完畢 Task.ContinueWith( task=>{ }); //執行完上一個任務后繼續執行,並將上一個任務(包括結果)傳遞給下一個代碼塊 一種是使用GetAwaiter方法。GetAwaiter方法返回一個TaskAwaiter結構,該結構有一個OnCompleted事件,只需對 OnCompleted事件賦值,即可在完成后調用該事件。 static void Main(string[] args) { Task<int> Task1 = Task.Run<int>(() => { return Enumerable.Range(1, 100).Sum(); }); var awaiter = Task1.GetAwaiter(); awaiter.OnCompleted(() => { Console.WriteLine("Task1 finished"); int result = awaiter.GetResult(); Console.WriteLine(result); // Writes result }); Thread.Sleep(1000); }
四、任務的中斷
var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; var task = Task.Factory.StartNew(() => { for (var i = 0; i < 1000; i++) { System.Threading.Thread.Sleep(1000); if (token.IsCancellationRequested) { Console.WriteLine("Abort mission success!"); return; } } }, token); //注冊cancel后要執行的代碼 token.Register(() => { Console.WriteLine("Canceled"); }); Console.WriteLine("Press enter to cancel task..."); Console.ReadKey(); //調用取消 tokenSource.Cancel();
五、任務的中斷取消
var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; var task = Task.Factory.StartNew(() => { for (var i = 0; i < 1000; i++) { System.Threading.Thread.Sleep(1000); if (token.IsCancellationRequested) { Console.WriteLine("Abort mission success!"); return; } } }, token); //注冊cancel后要執行的代碼 token.Register(() => { Console.WriteLine("Canceled"); }); Console.WriteLine("Press enter to cancel task..."); Console.ReadKey(); //調用取消 tokenSource.Cancel();
六、異常處理
異常處理;
對於某些匿名的Task(通過 Task.Run方法生成的,不調用wait,也不關心是否運行完成),某些情況下,記錄它們的異
常錯誤也是有必要的。這些異常稱作未觀察到的異常(unobserved exceptions)。可以通過訂閱一個全局的靜態事件
TaskScheduler.UnobservedTaskException來處理這些異常。只要當一個Task有異常,並且在被垃圾回收的時候,才會觸
發這一個事件。如果Task還處於被引用狀態,或者只要GC不回收這個Task,這個UnobservedTaskException事件就不會被
觸發
GC.Collect();
GC.WaitForPendingFinalizers();
七、Task的狀態
Created:表示默認初始化任務,但是“工廠創建的”實例直接跳過。
WaitingToRun: 這種狀態表示等待任務調度器分配線程給任務執行。
RanToCompletion:任務執行完畢。