前言:之前的文章介紹了了並行編程的一些基本的,也注重的講述了Task的一些使用方法,本篇很短,將會結束Task的基礎知識的介紹。
本篇的主要議題如下:
1. 獲取Task的狀態
2. 執行晚加載的Task(Lazily Task)
3. 常見問題的解決方案
1.獲取Task的狀態
在.NET並行編程還有一個已經標准化的操作就是可以獲取task的狀態,通過Task.Status屬性來得到的,這個屬性返回一個System.Threading.Tasks.TaskStatus的枚舉值。
如下:
Created:表明task已經被初始化了,但是還沒有加入到Scheduler中。
WatingForActivation:task正在等待被加入到Scheduler中。
WaitingToRun:已經被加入到了Scheduler,等待執行。
Running:task正在運行
WaitingForChildrenToComplete:表明父task正在等待子task運行結束。
RanToCompletion:表明task已經執行完了,但是還沒有被cancel,而且也這個task也沒有拋出異常。
Canceled:表明task已經被cancel了。(大家可以參看之前講述取消task的文章)
Faulted:表明task在運行的時候已經拋出了異常。
2. 執行晚加載的Task(Lazily Task)
晚加載,或者又名延遲初始化,主要的好處就是避免不必要的系統開銷。在並行編程中,可以聯合使用Lazy變量和Task<>.Factory.StartNew()做到這點。(Lazy變量時.NET 4中的一個新特性,這裡大家不用知道Lazy的具體細節)
Lazy變量只有在用到的時候才會被初始化。所以我們可以把Lazy變量和task的創建結合:只有這個task要被執行的時候才去初始化。
下面還是通過例子來講解:
static void Main(string[] args)
{
// define the function
Func<string> taskBody = new Func<string>(() =>
{
Console.WriteLine("Task body working...");
return "Task Result";
});
// create the lazy variable
Lazy<Task<string>> lazyData = new Lazy<Task<string>>(() =>
Task<string>.Factory.StartNew(taskBody));
Console.WriteLine("Calling lazy variable");
Console.WriteLine("Result from task: {0}", lazyData.Value.Result);
// do the same thing in a single statement
Lazy<Task<string>> lazyData2 = new Lazy<Task<string>>(
() => Task<string>.Factory.StartNew(() =>
{
Console.WriteLine("Task body working...");
return "Task Result";
}));
Console.WriteLine("Calling second lazy variable");
Console.WriteLine("Result from task: {0}", lazyData2.Value.Result);
// wait for input before exiting
Console.WriteLine("Main method complete. Press enter to finish.");
Console.ReadLine();
}
首先我們回想一下,在之前的系列文章中我們是怎麼定義一個task的:直接new,或者通過task的factory來創建,因為創建task的代碼是在main函數中的,所以只要new了一個task,那麼這個task就被初始化。現在如果用了Lazy的task,那麼現在我們初始化的就是那個Lazy變量了,而沒有初始化task,(初始化Lazy變量的開銷小於初始化task),只有當調用了lazyData.Value時,Lazy變量中包含的那個task才會初始化。(這裡歡迎大家提出自己的理解)
3.常見問題的解決方案
a. Task 死鎖
描述:如果有兩個或者多個task(簡稱TaskA)等待其他的task(TaskB)執行完成才開始執行,但是TaskB也在等待TaskA執行完成才開始執行,這樣死鎖就產生了。
解決方案:避免這個問題最好的方法就是:不要使的task來依賴其他的task。也就是說,最好不要你定義的task的執行體內包含其他的task。
例子:在下面的例子中,有兩個task,他們相互依賴:他們都要使用對方的執行結果。當主程序開始運行之後,兩個task也開始運行,但是因為兩個task已經死鎖了,所以主程序就一直等待。
static void Main(string[] args)
{
// define an array to hold the Tasks
Task<int>[] tasks = new Task<int>[2];
// create and start the first task
tasks[0] = Task.Factory.StartNew(() =>
{
// get the result of the other task,
// add 100 to it and return it as the result
return tasks[1].Result + 100;
});
// create and start the second task
tasks[1] = Task.Factory.StartNew(() =>
{
// get the result of the other task,
// add 100 to it and return it as the result
return tasks[1].Result + 100;
});
// wait for the tasks to complete
Task.WaitAll(tasks);
// wait for input before exiting
Console.WriteLine("Main method complete. Press enter to finish.");
Console.ReadLine();
}