最近研究多线程,感觉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);
}
}