C#中关于Task、Task.ContinueWith()和Task.WaitAll()的用法
C#多线程编程
在C#的多线程编程中,一般可以使用Thread Class来进行多线程运行,但从**.net4.0开始,引进了Task Class之后,官方更推荐用Task类来异步编程。
创建一个进程需要一定的开销和时间,特别是多个线程的时候,必须考虑创建和销毁线程需要的系统开销,这时就需要用到Thread pool线程池来管理线程,防止频繁的创建和销毁线程。但是,.net4.0之后,微软创建了一个优化的Task类,它默认会创建线程池来管理task,使用起来更加方便,系统开销更小。可参见stackoverflow上关于Task和Thread的回答,因此,学习和使用Task**是很有必要的。
Task使用方式
详细方法可以参阅codeproject的文章,写的比较详细具体。简单点来说的话,Task类的使用可以分成3种。
-
Task.Factory.StartNew(Action action)
-
Task.Run(Action action)
-
Task task = new Task(Action action);
task.Start();
当然还有带有返回值[Task](https://msdn.microsoft.com/en-us/library/dd321424.aspx)类,用法差不多。
至于如何终止**Task**,[codeproject的文章](http://www.codeproject.com/Articles/996857/Asynchronous-programming-and-Threading-in-Csharp-N)中也介绍了,在此不再赘述。
## Task.ContinueWith()
[Task.ContinueWith](https://msdn.microsoft.com/en-us/library/dd270696.aspx)的定义是
> Creates a continuation that executes asynchronously when the target Task completes.
就是当线程执行完毕之后,再异步运行一段程序。因为,当**Task**在后台运行时,比如进行大量的数据处理,多数情况下我们无法预知线程何时运行结束,除非进行轮询,但显然不是一个高效的方法,微软又提供了适用的方法,
举个简单控制台例子
```csharp
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Task ContinueWith test begins:");
doTask();
Thread.Sleep(1000);
Console.WriteLine("this is a main thread");
Console.ReadLine();
}
public static Task doTask()
{
return Task.Run(() => {
Console.WriteLine("task step1");
}).ContinueWith((preTask) => {
Console.WriteLine("task step2");
}).ContinueWith((preTask) => {
Thread.Sleep(1000);
Console.WriteLine("task step3");
});
}
}
}
输出结果为
Task ContinueWith test begins:
task step1
task step2
this is a main thread
task stpe3
从实际用法来看,执行完Task.Run()
之后接着异步运行ContinueWith()
的任务。
Task.WaitAll()的用法
进一步的,如何知道Task是否运行结束,可以使用Task.WaitAll()
Waits for all of the provided Task objects to complete execution
运行一个简单的控制台例子
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<Task> taskList = new List<Task>();
Console.WriteLine("Task WaitAll test begins:");
for (int i=0; i < 5; i++)
{
Task task = doTask(i);
taskList.Add(task);
}
Task.WaitAll(taskList.ToArray());
Console.WriteLine("this is a main thread");
Console.ReadLine();
}
public static Task doTask(int i)
{
return Task.Run(() => {
Console.WriteLine("task step1 of " + i.ToString());
}).ContinueWith((preTask) => {
Console.WriteLine("task step2 of " + i.ToString());
}).ContinueWith((preTask) => {
Console.WriteLine("task step3 of " + i.ToString());
});
}
}
}
创建了5个子线程,同时运行,运行结果是
Task.WaitAll是等待所有的Task运行结束,但是会阻塞当前线程,如果是在WinForm编程的话,为了防止UI主线程被阻塞,应该创建一个子线程来等待。