最近研究多线程,感觉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]
publicvoid 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]
publicvoid TestMethod2()
        {
TaskTask.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,是需要同步执行的。设计断点如下。

show slave status 返回 empty set taskspawn返回值_获取数据

show slave status 返回 empty set taskspawn返回值_d3_02

 

上面TestMethod3和TestMethod1基本相同,只是没有使用Wait方法。调试

结果显示,虽然DoOnFirst回休眠3秒钟,但是也会先执行DoOnFirst里面的return语句,然后再执行主线程里的Console语句。

二、组合任务之ContinueWith

接着写一个方法带有返回值的方法

intTask t)
Console.WriteLine("task {0}, t.Id);
Console.WriteLine("this task id {0}", Task.CurrentId);      
Console.WriteLine("方法33");
Thread.Sleep(3000);
return 33;
        }

1使用普通的continuewith方法调用

TestMethod]
publicvoid TestMethod3()
        {
varnewTask<int>(() => DoOnFirst());
Task<int> t3 = t1.ContinueWith(preT => DoOnThird(t1));
            t1.Start();
var//返回结果是11
var//返回结果是33,是ContinueWith这个方法的结果
Console.WriteLine("结果:{0}", a1);
        }

2几个任务组合在一起

TestMethod]
publicvoid TestMethod5()
        {
varnewTask<int>(() => DoOnFirst());
var t4 = t1.ContinueWith(preT => DoOnThird(t1)).ContinueWith(preT =>
            {
Console.WriteLine("前一个结果是 {0}.",
                    preT.Result);
return 44;
            });
            t1.Start();

var//返回结果是11

var//返回结果是44,说明如果使用一个任务后面跟着几个ContinueWith时候,会返回最后一个continuewith方法的结果

//注意:如果想得到前面几个continue方法的结果,可以通过把方法里的参数(即前一个任务的相关信息,包括id,result等)传递时候,获取到前一个任务的结果,然后存储起来    

        }

3开启任务的时候,直接跟着continue。以及最后一个continuewith方法无返回值的情况

TestMethod]
publicvoid TestMethod6()
        {
varTask.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//返回结果是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]
publicvoid TestMethod7()
        {
varTask.Run(() =>{
return DoOnFirst();
                }
            );
varTask.Run(() =>
            {
Console.WriteLine("doing some task {0}", Task.CurrentId);
Thread.Sleep(2000);
return 22;
            });
varTask.Run(() =>
            {
Console.WriteLine("doing some task {0}", Task.CurrentId);
Thread.Sleep(4000);
return 33;
            });
//等待任务完成,使用Task的静态方法WaitAll
Task.WaitAll(task1, task2, task3);  
//获取数据
int//返回11
int//返回22
int//返回33
        }

2.使用Task.WhenAll,返回的结果是一个数组,包含所有任务的值

TestMethod]
publicvoid 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"]
foreachintin results)
            {
Console.WriteLine(result);
            }
        }

3. 使用Task.WhenAny,一个一个结果的返回

TestMethod]
publicvoid TestMethod9()
        {
varnewList<Task<int>>();
forint i = 1; i < 4; i++)
            {
int counter = i;
varnewTask<int>(() =>
                {
Thread.Sleep(1000);
return//不要用i,用i会导致进入修改的闭包
                });
                tasks.Add(task);
                task.Start();
            }
while (tasks.Count > 0)
            {
Task<int> completedTask = Task.WhenAny(tasks).Result;//只要任意一个完成,就赶回结果,所以是一次只返回一个结果
//删除已经完成的任务
Console.WriteLine("A task has been completed with result {0}.", completedTask.Result);
            }
        }