Task和continuewith的返回值問題
最近研究多線程,感覺Task的返回值很要,特別是ContinueWith或者是使用task.WhenAll或者task.WhenAny的時候,需要確定到底會出現什麽樣的結果。在網上看了很多人寫的文章,感覺參雜的信息太多,所以寫了這篇簡單的只講task返回值的文章,盡量減少其他元素的幹擾。
本文內容都是通過單元測試實踐得到(visual studio2013上應用了ReSharper)的結果,不是猜想的。
一、task直接返回值
1.定義一個返回int類型的普通方法,定義一個Task調用這個方法
int DoOnFirst()
{
Console.WriteLine("doing some task {0}", Task.CurrentId);
Thread.Sleep(3000);
return 11;
}
[TestMethod]
public void TestMethod1()
{
//Task<int> t1 = new Task(()=>DoOnFirst);
//t1.Start();
Task<int> t1 = Task.Factory.StartNew(() =>
{
return DoOnFirst();
});
t1.Wait();
var a1 = t1.Result;
}
結果輸出11
2.調用的方法無返回值
創建一個無返回值的方法
//action的模式
void DoOnSecond()
{
Console.WriteLine("do some cleanup 22");
//Thread.Sleep(3000);
//return 22;
}
調用無返回值的的方法
[TestMethod]
public void TestMethod2()
{
Task t2 = Task.Factory.StartNew(() =>
{
//由於DoOnSecond沒有返回值,所以不能用return返回
// return DoOnSecond();
DoOnSecond();
});
t2.Wait();
// var a2 = t2.Result;//因為DoOnSecond方法沒有返回值,所以t2沒有result
Console.WriteLine("結果:", t2.ToString());
}
此時t2的信息如下,沒有Result屬性。
Id = 1, Status = Running, Method = "Void <TestMethod2>b__5()"
3.另外其實在TestMethod1中其實不需要做wait等待的,因為此時主線程為了獲取t1的Result,是需要同步執行的。設計斷點如下。
上面TestMethod3和TestMethod1基本相同,只是沒有使用Wait方法。調試
結果顯示,雖然DoOnFirst回休眠3秒鐘,但是也會先執行DoOnFirst裏面的return語句,然後再執行主線程裏的Console語句。
二、組合任務之ContinueWith
接著寫一個方法帶有返回值的方法
int DoOnThird(Task t)
{ Console.WriteLine("task {0} finished", t.Id);
Console.WriteLine("this task id {0}", Task.CurrentId);
Console.WriteLine("方法33");
Thread.Sleep(3000);
return 33;
}
1使用普通的continuewith方法調用
[TestMethod]
public void TestMethod3()
{
var t1 = new Task<int>(() => DoOnFirst());
Task<int> t3 = t1.ContinueWith(preT => DoOnThird(t1));
t1.Start();
var a1 = t1.Result;//返回結果是11
var a3 = t3.Result;//返回結果是33,是ContinueWith這個方法的結果
Console.WriteLine("結果:{0}", a1);
}
2幾個任務組合在一起
[TestMethod]
public void TestMethod5()
{
var t1 = new Task<int>(() => DoOnFirst());
var t4 = t1.ContinueWith(preT => DoOnThird(t1)).ContinueWith(preT =>
{
Console.WriteLine("前一個結果是 {0}.",
preT.Result);
return 44;
});
t1.Start();
var a1 = t1.Result;//返回結果是11
var a4 = t4.Result;//返回結果是44,說明如果使用一個任務後面跟著幾個ContinueWith時候,會返回最後一個continuewith方法的結果
//註意:如果想得到前面幾個continue方法的結果,可以通過把方法裏的參數(即前一個任務的相關信息,包括id,result等)傳遞時候,獲取到前一個任務的結果,然後存儲起來
}
3開啟任務的時候,直接跟著continue。以及最後一個continuewith方法無返回值的情況
[TestMethod]
public void TestMethod6()
{
var t1 = Task.Run(() => DoOnFirst()).ContinueWith(preT =>
{
Console.WriteLine("前一個結果是 {0}.",
preT.Result);
return 44;
});
var t4 = t1.ContinueWith(preT => DoOnThird(t1)).ContinueWith(preT =>
{
Console.WriteLine("前一個結果是 {0}.",
preT.Result);
});
// t1.Start();//不能對延續任務調用start
var a1 = t1.Result;//返回結果是44,說明一組任務一起運行的時候,會取最後一個任務的結果,做為返回值
t1.Wait();
// var a4 = t4.Result;//此時:t4的信息是 Id = 11, Status = WaitingForActivation, Method = "Void <TestMethod5>b__11(System.Threading.Tasks.Task`1[System.Int32])"
//沒有Result這個屬性,所以無法獲取屬性值,這是因為最後一個continuewith的方法沒有返回值,因此也就沒有result
}
三、並行運行任務之WhenAll和WhenAny
1.在我目前做的項目裏,有時候需要同時從兩三個數據源裏獲取數據,而從每個數據源取數據可能花費的時間都比較長,如果等待一個個地執行,需要花費較長的時間。於是就把幾個操作都放到了task下面,然後等待他們都完成了,再取出結果。做法如下
[TestMethod]
public void TestMethod7()
{
var task1 = Task.Run(() =>{
return DoOnFirst();
}
);
var task2 = Task.Run(() =>
{
Console.WriteLine("doing some task {0}", Task.CurrentId);
Thread.Sleep(2000);
return 22;
});
var task3 = Task.Run(() =>
{
Console.WriteLine("doing some task {0}", Task.CurrentId);
Thread.Sleep(4000);
return 33;
});
//等待任務完成,使用Task的靜態方法WaitAll
Task.WaitAll(task1, task2, task3);
//獲取數據
int a1 = task1.Result;//返回11
int a2 = task2.Result;//返回22
int a3 = task3.Result;//返回33
}
2.使用Task.WhenAll,返回的結果是一個數組,包含所有任務的值
[TestMethod]
public void TestMethod8()
{
Task<int> t1 = Task.Run(() =>
{
Console.WriteLine("doing some task {0}", Task.CurrentId);
Thread.Sleep(2000);
return 111;
});
Task<int> t2 = Task.Run(() =>
{
Console.WriteLine("doing some task {0}", Task.CurrentId);
Thread.Sleep(2000);
return 222;
});
int[] results = Task.WhenAll(t1, t2).Result;//返回的結果是["111","222"]
foreach (int result in results)
{
Console.WriteLine(result);
}
}
3. 使用Task.WhenAny,一個一個結果的返回
[TestMethod]
public void TestMethod9()
{
var tasks = new List<Task<int>>();
for (int i = 1; i < 4; i++)
{
int counter = i;
var task = new Task<int>(() =>
{
Thread.Sleep(1000);
return counter;//不要用i,用i會導致進入修改的閉包
});
tasks.Add(task);
task.Start();
}
while (tasks.Count > 0)
{
Task<int> completedTask = Task.WhenAny(tasks).Result;//只要任意一個完成,就趕回結果,所以是一次只返回一個結果
tasks.Remove(completedTask);//刪除已經完成的任務
Console.WriteLine("A task has been completed with result {0}.", completedTask.Result);
}
}
Task和continuewith的返回值問題