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);

            }

        }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM