zoukankan      html  css  js  c++  java
  • 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);

                }

            }

    理论指导实践,实践是检验真理的唯一标准。
  • 相关阅读:
    中文短文本分类
    词袋和词向量模型
    【NLP-2017-SA】翻译-Recurrent Attention Network on Memory for Aspect Sentiment Analysis(2017emnlp)
    过拟合和欠拟合问题总结
    【Docker-6】-Docker删除运行过的镜像
    【Docker-5】-Docker运行自己的镜像
    【Docker-3】-Docker入门命令解读
    【Docker-4】-Docker启动nginx
    【Docker-2】windows下安装docker
    【Docker-1】docker简介
  • 原文地址:https://www.cnblogs.com/zhousong/p/9063124.html
Copyright © 2011-2022 走看看