首页 > 代码库 > 多线程——Task
多线程——Task
背景:
以前想用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获得对一个同步上下文任务调度起的引用。
多线程——Task