在前一篇中,了解了Task的基本用法
如果一個方法返回Task,Task<T>,如何獲取Task的返回值,獲取值的過程會阻塞線程嗎?
static void Main(string[] args){var result = DoWorkAsync().Result;Console.WriteLine(result);Console.WriteLine("我會什麼時候顯示");Console.ReadKey();}static Task<string> DoWorkAsync(){return Task<string>.Factory.StartNew(() =>{Thread.Sleep(3000);return "hello";});}
可見,Task的Result屬性可以獲取返回值,而且,獲取返回值的過程線程是被阻塞的。
是否可以不阻塞線程,又能拿到某個線程的返回值呢?ContinueWith方法在某個線程結束之後進行,但同時不會阻塞線程。
static void Main(string[] args){DoWorkAsync().ContinueWith((pre) =>{Console.WriteLine(pre.Result);});Console.WriteLine("我會什麼時候顯示");Console.ReadKey();}
但ContinueWith總會在某個線程結束之後進行,是否可以對ContinueWith的過程控制一下呢?
static void Main(string[] args){DoWorkAsync().ContinueWith((pre) =>{Console.WriteLine(pre.Result);}, TaskContinuationOptions.NotOnFaulted);DoWorkAsync().ContinueWith((pre) =>{Console.WriteLine(pre.Exception);},TaskContinuationOptions.OnlyOnFaulted);Console.WriteLine("我會什麼時候顯示");Console.ReadKey();}
以上,當沒有錯誤的時候就把返回值顯示出來,有錯誤就把錯誤信息顯示出來。
還可以通過Task的實例方法IsCompleted來判斷一個線程是否完成。
static void Main(string[] args){var doWorkTask = DoWorkAsync();if (doWorkTask.IsCompleted){Console.WriteLine(doWorkTask.Result);}else{doWorkTask.ContinueWith((pre) =>{Console.WriteLine(pre.Result);}, TaskContinuationOptions.NotOnFaulted);doWorkTask.ContinueWith((pre) =>{Console.WriteLine(pre.Exception);}, TaskContinuationOptions.OnlyOnFaulted);}Console.WriteLine("我會什麼時候顯示");Console.ReadKey();}
Task的Status屬性,以及結合TaskStatus枚舉,可以判斷Task的狀態。
static void Main(string[] args){var httpClient = new HttpClient();Task<string> baiduTask = httpClient.GetStringAsync("http://www.baidu.com");var httpClient2 = new HttpClient();Task<string> sinaTask = httpClient2.GetStringAsync("http://www.sina.com.cn");//等上面2個任務完成時這裡再開始Task<string[]> task = Task.WhenAll(baiduTask, sinaTask);task.ContinueWith(stringArray =>{//如果任務完成if (task.Status == TaskStatus.RanToCompletion){for (int i = 0; i < stringArray.Result.Length;i++){Console.WriteLine(stringArray.Result[i].Substring(0,100));}}else if (task.Status == TaskStatus.Canceled) //如果被取消{Console.WriteLine("{0}這個任務被取消了",task.Id);}else //發生錯誤{Console.WriteLine("發生錯誤了~~");foreach (var item in task.Exception.InnerExceptions){Console.WriteLine(item.Message);}}});Console.ReadKey();}
如果要控制Task的生命周期,可以考慮使用TaskCompletionSource<T>。
static void Main(string[] args){AsyncFactory.GetIntAsync().ContinueWith((prev) =>{if (prev.Status == TaskStatus.RanToCompletion){Console.WriteLine(prev.Result);}else if (prev.Status == TaskStatus.Canceled){Console.WriteLine("任務被取消");}else{Console.WriteLine("發生錯誤哦");Console.WriteLine(prev.Exception);}});Console.ReadKey();}}public static class AsyncFactory{public static Task<int> GetIntAsync(){var tsc = new TaskCompletionSource<int>();var timer = new System.Timers.Timer(2000);timer.AutoReset = false;timer.Elapsed += (s, e) =>{tsc.SetResult(10);timer.Dispose();};timer.Start();return tsc.Task;}}
以上,通過TaskCompletionSource<T>的SetResult給線程設置返回值,通過TaskCompletionSource<T>的.Task有獲取了線程。
另外,從.NET 4.5開始,Task的靜態方法FromResult,接收T類型,返回Task<T>。
static void Main(string[] args){var intTask = GetIntAsync();if (intTask.Status == TaskStatus.RanToCompletion){Console.WriteLine(intTask.Result);}else if (intTask.Status == TaskStatus.Canceled){Console.WriteLine("任務被取消");}else{Console.WriteLine("發生錯誤哦");Console.WriteLine(intTask.Exception);}Console.ReadKey();}static Task<int> GetIntAsync(){return Task.FromResult(10);}