Task是从 .NET Framework 4 开始引入的一项基于队列的异步任务(TAP)模式,从 .NET Framework 4.5 开始,任何使用 async/await 进行修饰的方法,都会被认为是一个异步方法;实际上,这些异步方法都是基于队列的线程任务,从你开始使用 Task 去运行一段代码的时候,实际上就至关于开启了一个线程,默认状况下,这个线程数由线程池 ThreadPool 进行管理的。git
Task 的使用用方法很是简单,一行代码就能够开始一个异步任务github
static void EasyTask() { // 执行一个无返回值的任务 Task.Run(() => { Console.WriteLine("runing..."); }); // 执行一个返回 int 类型结果的任务 Task.Run<int>(() => { return new Random().Next(); }); // 声明一个任务,仅声明,不执行 Task t = new Task(() => { Console.WriteLine(""); }); }
上面的代码看起来很是简单,只须要一行代码就完成了一个异步任务线程,先不要去深究其背后的原理,对于新手来讲,先解决能用,再去了解为何能够这样使用,否则,一开始就失去了学习的信心服务器
static void Factory() { List<Task<int>> tasks = new List<Task<int>>(); TaskFactory factory = new TaskFactory(); tasks.Add(factory.StartNew<int>(() => { return 1; })); tasks.Add(factory.StartNew<int>(() => { return 2; })); foreach (var t in tasks) { Console.WriteLine("Task:{0}", t.Result); } }
上面的代码使用 TaskFactory 建立并运行了两个异步任务,同时把这两个任务加入了任务列表 tasks 中,而后当即迭代此 tasks 获取异步任务的执行结果,使用 TaskFactory 工厂类,能够建立一组人物,而后依次执行它们多线程
异步任务中发生异常会致使任务抛出 TaskCancelException 的异常,仅表示任务退出,程序应当捕获该异常;而后,当即调用 Task 进行状态判断,获取内部异常并发
static void SimpleTask() { var task = Task.Run(() => { Console.WriteLine("SimpleTask"); Task.Delay(1000).Wait(); throw new Exception("SimpleTask Error"); }); try { task.Wait(); } catch (Exception ex) { Console.WriteLine(ex.Message); } if (task.IsCompletedSuccessfully) { Console.WriteLine("IsCompleted"); } }
上面的代码模拟了 Task 内部发生的异常,并捕获了异常,一般状况下,推荐使用 Task 的任务状态判断以进行下一步的任务处理(若是须要),若是仅仅是简单的执行一个异步任务,直接捕获异常便可,这里使用了状态判断,若是任务已完成,则打印一则消息:IsCompleted;很明显,在上面的代码中,此 “IsCompleted” 消息并不会被打印到控制台
注意,这里使用了 task.IsCompletedSuccessfully 而不是 task.IsCompleted,这二者的区别在于,前者只有在任务正常执行完成,无异常,无中途退出指令的状况下才会表示已完成,而 task.IsCompleted 则仅仅表示“任务完成”dom
在 WinForm/WPF 应用程序中,也经常须要在 UI 上开辟异步任务,一般状况下,窗体控件仅容许建立其的线程访问,在没有 Task 的时代,处理异步上下文到同步上下文是一件很是复杂的事情,在 Task 出现之后,提供了 TaskScheduler 任务调度器,让咱们能够很是方便的在异步线程中访问 UI 线程的资源异步
static void TaskSynchronizationContext() { var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext(); var t1 = Task.Factory.StartNew<int>(() => { return 1; }); t1.ContinueWith((atnt) => { // 从这里访问 UI 线程的资源 Console.WriteLine("从这里访问 UI 线程的资源"); }, UISyncContext); }
从上面的代码能够发现,仅仅须要调用 TaskScheduler.FromCurrentSynchronizationContext() 得到当前线程的同步上下文,而后在执行异步任务的时候传入,便可访问当前线程建立的 UI 资源async
一个异步任务老是处于队列中,任务队列基于先进先出的原则,最新进入队列的任务老是最早被执行;可是,在多线程的环境下,最早执行并不意味着最早结束,意识到这一点很重要,每一个任务可调度的资源和处理的进度决定了任务的完成时间。
默认状况下,全部的任务都使用 ThreadPool 的资源,当你开启一个 Task 的时候,实际上,是由 ThreadPool 分配了一个线程,ThreadPool 的上限取决于不少方面的因素,例如虚拟内存的大小,当 Task 开启的数量超过ThreadPool 的上限的时候,Task 将进入排队状态,能够手动设置 ThreadPool 的大小学习
static void SetThreadPool() { var available = ThreadPool.SetMaxThreads(8, 16); Console.WriteLine("Result:{0}", available); }
上面的代码表示设置当前程序可以使用的线程池大小,可是,SetMaxThreads 的值不该该小于托管服务器的 CPU 核心数量,不然,变量 available 的值将显示为 false,表示未成功设置线程池上限
注意:ThreadPool 上的全部线程都是后台线程,也就是说,其IsBackground属性是true,在托管程序退出后,ThreadPool 也将会退出。线程
在建立 Task 的时候,咱们可能须要作一些长时间运行的业务,这个时候若是使用默认的 ThreadPool 资源,在并发状态下,这是不合适的,由于该任务老是长时间的占用线程池中的资源,致使线程池数量受限,这种状况下,能够在建立任务的时候使用指定 TaskCreationOptions.LongRunning 方式建立 Task
static void LongTask() { Task.Factory.StartNew(() => { Console.WriteLine("LongRunning Task"); }, TaskCreationOptions.LongRunning); }
上面的代码看起来和建立普通的 Task 任务并无多大的区别,惟一不一样的是,在参数中传入了 TaskCreationOptions.LongRunning,指定这个是一个 LongRunning 类型的任务,当TaskFactory 收到这样一个类型的任务时,将会为这个任务开辟一个独立的线程,而不是从 ThreadPool 中建立
Task 内部提供多种多样的基于队列的链式任务管理方法,经过使用这些快捷方式,可让异步队列有序的执行,好比ContinueWith(),ContinueWhenAll(),ContinueWhenAny(),WaitAll(),WaitAny(),WhenAll(),WhenAny()
static void WithTask() { var order1 = Task.Run(() => { Console.WriteLine("Order 1"); }); // 匿名委托将等待 order1 执行完成后执行,并将 order1 对象做为参数传入 order1.ContinueWith((task) => { Console.WriteLine("Order 1 Is Completed"); }); var t1 = Task.Run(() => { Task.Delay(1500).Wait(); Console.WriteLine("t1"); }); var t2 = Task.Run(() => { Task.Delay(2000).Wait(); Console.WriteLine("t2"); }); var t3 = Task.Run(() => { Task.Delay(3000).Wait(); Console.WriteLine("t3"); }); Task.WaitAll(t1, t2, t3); // t1,t2,t3 完成后输出下面的消息 Console.WriteLine("t1,t2,t3 Is Complete"); var t4 = Task.Run(() => { Task.Delay(1500).Wait(); Console.WriteLine("t4"); }); var t5 = Task.Run(() => { Task.Delay(2000).Wait(); Console.WriteLine("t5"); }); var t6 = Task.Run(() => { Task.Delay(3000).Wait(); Console.WriteLine("t6"); }); Task.WaitAny(t4, t5, t6); // 当任意任务完成时,输出下面的消息,目前按延迟时间计算,在 t4 完成后当即输出下面的信息 Console.WriteLine("t4,t5,t6 Is Complete"); var t7 = Task.Run(() => { Task.Delay(1500).Wait(); Console.WriteLine("t7"); }); var t8 = Task.Run(() => { Task.Delay(2000).Wait(); Console.WriteLine("t8"); }); var t9 = Task.Run(() => { Task.Delay(3000).Wait(); Console.WriteLine("t9"); }); var whenAll = Task.WhenAll(t7, t8, t9); // WhenAll 不会等待,因此这里必须显示指定等待 whenAll.Wait(); // 当全部任务完成时,输出下面的消息 Console.WriteLine("t7,t8,t9 Is Complete"); var t10 = Task.Run(() => { Task.Delay(1500).Wait(); Console.WriteLine("t10"); }); var t11 = Task.Run(() => { Task.Delay(2000).Wait(); Console.WriteLine("t11"); }); var t12 = Task.Run(() => { Task.Delay(3000).Wait(); Console.WriteLine("t12"); }); var whenAny = Task.WhenAll(t10, t11, t12); // whenAny 不会等待,因此这里必须显示指定等待 whenAny.Wait(); // 当任意任务完成时,输出下面的消息,目前按延迟时间计算,在 t10 完成后当即输出下面的信息 Console.WriteLine("t10,t11,t12 Is Complete"); }
值得注意的是,当调用 WhenAll 方法时,会返回执行任务的状态,此状态是全部任务的统一状态,若是执行了 3 个任务,而其中一个出错,则返回任务状态表示为:Faulted,若是任意任务被取消,则状态为:Canceled;
当调用 WhenAny() 方法时,表示任意任务完成便可表示完成,此时,会返回最早完成的任务信息
注意:WhenAll 和 WhenAny 方法正常执行,无异常,无取消,则所返回的完成状态表示为:RanToCompletion
https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.TaskDemo