1. 程式人生 > >Task和continuewith的返回值問題

Task和continuewith的返回值問題

單元 很多 edt lang been 參數 TP AR pan

最近研究多線程,感覺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的返回值問題