Task和continuewith的返回值问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的返回值问题的主要内容,如果未能解决你的问题,请参考以下文章

C#使用异步操作时的注意要点(翻译)

组合ContinueWith

如何使用 ContinueWith 正确管理任务中的异常

[C#]Task.ContinueWith使用实例

[C#]Task.ContinueWith使用实例

在 Task ContinueWith TaskScheduler.FromCurrentSynchronizationContext 的 ShowDialog 中打开表单时,应用程序会冻结