背景:
以前想用Semaphore来处理并发访问资源的问题,后来把方案拿给前辈们看的时候,他们说这样也行,但是最好用Task处理,比较简单。所以,顺便学习了下Task.
使用task类创建并执行简单任务
等待任务的完成并获取返回值
使用ContinueWith方法在任务完成时启动一个新任务
创建父子任务和任务工厂的使用
<span style="font-size:12px;">namespace 创建父子任务和任务工厂的使用
{
class Program
{
//通过task类创建的任务是顶级任务,可以通过使用TaskCreationOptions .AttachedToParent 标识把这些任务与创建他的任务相关联
//所有子任务完成以后父任务才会结束操作
//
static void Main(string[] args)
{
#region 创建父子任务Demo
//Task<string[]> parent = new Task<string[]>(state => {
// Console.WriteLine(state);
// string[] result = new string[2];
// //创建并启动子任务
// new Task(() => { result[0] = "任务1。。。"; },TaskCreationOptions .AttachedToParent ).Start ();
// new Task(() => { result [1]="任务2。。。";},TaskCreationOptions .AttachedToParent ).Start ();
// return result ;
// },"这里是父任务,并在处理过程中创建多个子任务,所有子任务完成以后才会执行。");
// //任务处理完成后执行的操作
// parent .ContinueWith (t=>{
// Array.ForEach(t.Result, r => Console.WriteLine(r));
// });
// //启动父任务
// parent .Start ();
// Console .ReadKey ();
#endregion
#region 任务工厂的使用
Task parent=new Task(()=>{
CancellationTokenSource cts = new CancellationTokenSource();//为什么不包含一个参数的构造函数??????
//创建任务工厂
TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
//添加一组具有相同状态的子任务
Task[] task=new Task[]{
tf.StartNew (()=>{Console.WriteLine("我是任务工厂里的第一个任务");}),
tf.StartNew (()=>{Console .WriteLine("我是任务工厂里的第二个任务");}),
tf.StartNew (()=>{Console .WriteLine("我是任务工厂里的第三个任务");})
};
});
parent .Start();
Console.ReadKey();
#endregion
}
}
}</span>
任务内部的实现和调度
任务内部有一组构成任务状态的属性,表示任务的唯一ID,表示任务的执行状态(TaskStatus),任务创建时提供的回调函数的引用和传递给回调函数的数据对象AsyncState,对任务创建时的任务调度对象(TaskScheduler)的引用,对父任务的引用以及对执行上下文的引用和ManualResetEventSlim对象的引用。
Task和Task<TResult>类都实现了标准的释放资源的接口,允许在任务完成处理的时候使用Dispose方法释放资源(关闭ManualResetEventSlim对象实例)。可以使用Task类的CurrentID属性获得正在执行的任务的ID,如果没有任务在执行CurrentID返回的值为null,currentID是一个int,可空类型的属性。任务执行的生命周期通过TaskStatus类似的一个值来表示,TaskStatus所包含的值为taskstatus
namespace 任务内部的实现和调度
{
class Program
{
static void TestDemo()
{
//获得同步上下文任务调度器
TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();//表示一个处理将任务排队到线程中的低级工作对象
//创建任务,并采用默认任务调度(线程池任务调度器)执行任务
Task<int> task = new Task<int>(() =>
{
//执行复杂的计算任务
Thread.Sleep(2000);//当前线程暂停2000
int sum = 0;
for (int i = 0; i < 100; i++)
{
sum += i;
}
return sum;
});
var cts = new CancellationTokenSource();
//任务完成时启动一个后续任务,并采用同步上下文任务调度器调度任务更新UI组件
task.ContinueWith(t => { Console.WriteLine("采用SynchronizationContextTaskScheduler任务调度器更新UI。\r\n计算结果是:" + task.Result.ToString()); }, cts.Token, TaskContinuationOptions.AttachedToParent, m_syncContextTaskScheduler);
task.Start();
}
static void Main(string[] args)
{
TestDemo();
}
}
#region TaskStatus所包含的值:
// public enum TaskStatus
//{
// Created = 0,
// WaitingForActivation = 1,
// WaitingToRun = 2,
// Running = 3,
// WaitingForChildrenToComplete = 4,
// RanToCompletion = 5,
// Canceled = 6,
// Faulted = 7
//}
#endregion
}
我们可以通过Task类的Exception属性获得任务在执行过程中的所有异常,Exception是一个AggregateException类型的属性。Task类提供了IsCanceled、IsCompleted、IsFaulted属性来获得任务的完成状态。通过ContinueWith、ContinueWhenAll、ContinueWhenAny和FromAsync创建的后续任务都处于WaitingForActivation状态,这个状态的任务会在父任务完成后自动执行。
在任务内部由TaskScheduler类调度任务的执行,该类是一个抽象类,FCL中从他派生了两个派生类:ThreadPoolTaskScheduler线程池任务调度器和SynchronizationContextTaskScheduler同步上下文任务调度器。所有任务默认都是采用ThreadPoolTaskScheduler调度任务,他是采用线程池来执行任务,可以通过TaskScheduler类的静态属性Default获得对默认任务调度器的引用。SynchronizationContextTaskScheduler任务调度器能够用在Windowform、WPF等应用程序,他的任务调度是采用的GUI线程,所以他能同步更新UI组件,可以通过TaskScheduler类的静态方法FromCurrentSynchronizationContext获得对一个同步上下文任务调度起的引用。