当前位置: 首页 > news >正文

网站设计团队介绍广东建设工程网站

网站设计团队介绍,广东建设工程网站,wordpress5.1更新,可口可乐营销案例分析传送门#xff1a;异步编程系列目录…… 最近我为大家陆续介绍了“IAsyncResult异步编程模型 (APM)”和“基于事件的异步编程模式(EAP)”两种异步编程模型。在.NET4.0 中Microsoft又为我们引入了新的异步编程模型“基于任务的异步编程模型(TAP)”#xff0c;并且推荐我们在开… 传送门异步编程系列目录……        最近我为大家陆续介绍了“IAsyncResult异步编程模型 (APM)”和“基于事件的异步编程模式(EAP)”两种异步编程模型。在.NET4.0 中Microsoft又为我们引入了新的异步编程模型“基于任务的异步编程模型(TAP)”并且推荐我们在开发新的多线程应用程序中首选TAP在.NET4.5中更是对TPL库进行了大量的优化与改进。那现在我先介绍下TAP具有哪些优势 1.        目前版本(.NET4.X)的任务调度器(TaskScheduler)依赖于底层的线程池引擎。通过局部队列的任务内联化(task inlining)和工作窃取机制可以为我们提升程序性能。 2.        轻松实现任务等待、任务取消、延续任务、异常处理System.AggregateException、GUI线程操作。 3.        在任务启动后可以随时以任务延续的形式注册回调。 4.        充分利用现有的线程避免创建不必要的额外线程。 5.        结合C#5.0引入async和await关键字轻松实现“异步方法”。   示例源码异步编程.NET 4.5 基于任务的异步编程模型(TAP).rar   术语 APM              异步编程模型Asynchronous Programming Model EAP               基于事件的异步编程模式Event-based Asynchronous Pattern TAP               基于任务的异步编程模式Task-based Asynchronous Pattern TPL                任务并行库Task Parallel Library   理解CLR线程池引擎、理解全局队列、理解线程的局部队列及性能优势 1.        CLR线程池引擎 CLR线程池引擎维护了一定数量的空闲工作线程以支持工作项的执行并且能够重用已有的线程以避免创建新的不必要的线程所花费的昂贵的处理过程。并且使用爬山算法hill-climbing algorithm检测吞吐量判断是否能够通过更多的线程来完成更多的工作项。这个算法的判断依据是工作项所需某些类型资源的可用情况例如CPU、网络带宽或其他。此外这个算法还会考虑一个饱和点即达到饱和点的时候创建更多地线程反而会降低吞吐量。线程池的详细介绍请看《异步编程使用线程池管理线程》 目前版本的TAP的任务调度器TaskScheduler基于CLR线程池引擎实现。当任务调度器TaskScheduler开始分派任务时 1)        在主线程或其他并没有分配给某个特定任务的线程的上下文中创建并启动的任务这些任务将会在全局队列中竞争工作线程。这些任务被称为顶层任务。 2)        然而如果是在其他任务的上下文中创建的任务子任务或嵌套任务这些任务将被分配在线程的局部队列中。 嵌套任务 是在另一个任务的用户委托中创建并启动的任务。 子任务 是使用TaskCreationOptions.AttachedToParent选项创建顶层任务的嵌套任务或延续任务或使用TaskContinuationOptions.AttachedToParent选项创建的延续任务的嵌套任务或延续任务。应用程序使用TaskCreationOptions.DenyChildAttach选项创建父任务。此选项指示运行时会取消子任务的AttachedToParent规范 如果你不想特定的任务放入线程的局部队列那么可以指定TaskCreationOptions.PreferFairness或TaskContinuationOptions.PreferFairness枚举参数。使Task与ThreadPool.QueueUserWorkItem行为相同 2.        线程池的全局队列        当调用ThreadPool.QueueUserWorkItem()添加工作项时该工作项会被添加到线程池的全局队列中。线程池中的空闲线程以FIFO的顺序将工作项从全局队列中取出并执行但并不能保证按某个指定的顺序完成。        线程的全局队列是共享资源所以内部会实现一个锁机制。当一个任务内部会创建很多子任务时并且这些子任务完成得非常快就会造成频繁的进入全局队列和移出全局队列从而降低应用程序的性能。基于此原因线程池引擎为每个线程引入了局部队列。 3.        线程的局部队列为我们带来两个性能优势任务内联化(task inlining)和工作窃取机制。 1)        任务内联化(task inlining)----活用顶层任务工作线程 我们用一个示例来说明 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 static void Main(string[] args) {     Task headTask new Task(() gt;     {         DoSomeWork(null);     });     headTask.Start();     Console.Read(); } private static void DoSomeWork(object obj) {     Console.WriteLine(任务headTask运行在线程“{0}”上,         Thread.CurrentThread.ManagedThreadId);       var taskTop new Task(() gt;     {         Thread.Sleep(500);         Console.WriteLine(任务taskTop运行在线程“{0}”上,             Thread.CurrentThread.ManagedThreadId);     });     var taskCenter new Task(() gt;     {         Thread.Sleep(500);         Console.WriteLine(任务taskCenter运行在线程“{0}”上,             Thread.CurrentThread.ManagedThreadId);     });     var taskBottom new Task(() gt;     {         Thread.Sleep(500);         Console.WriteLine(任务taskBottom运行在线程“{0}”上,             Thread.CurrentThread.ManagedThreadId);     });     taskTop.Start();     taskCenter.Start();     taskBottom.Start();     Task.WaitAll(new Task[] { taskTop, taskCenter, taskBottom }); } 结果 分析目前内联机制只有出现在等待任务场景        这个示例我们从Main方法主线程中创建了一个headTask顶层任务并开启。在headTask任务中又创建了三个嵌套任务并最后WaitAll() 这三个嵌套任务执行完成(嵌套任务安排在局部队列)。此时出现的情况就是headTask任务的线程被阻塞而“任务内联化”技术会使用阻塞的headTask的线程去执行局部队列中的任务。因为减少了对额外线程需求从而提升了程序性能。        局部队列“通常”以LIFO的顺序抽取任务并执行而不是像全局队列那样使用FIFO顺序。LIFO顺序通常用有利于数据局部性能够在牺牲一些公平性的情况下提升性能。 数据局部性的意思是运行最后一个到达的任务所需的数据都还在任何一个级别的CPU高速缓存中可用。由于数据在高速缓存中任然是“热的”因此立即执行最后一个任务可能会获得性能提升。 2)        工作窃取机制----活用空闲工作线程 当一个工作线程的局部队列中有很多工作项正在等待时而存在一些线程却保持空闲这样会导致CPU资源的浪费。此时任务调度器TaskScheduler会让空闲的工作线程进入忙碌线程的局部队列中窃取一个等待的任务并且执行这个任务。   由于局部队列为我们带来了性能提升所以我们应尽可能地使用TPL提供的服务任务调度器TaskScheduler而不是直接使用ThreadPool的方法。   任务并行Task 一个任务表示一个异步操作。任务运行的时候需要使用线程但并不是说任务取代了线程理解这点很重要。事实上在《异步编程.NET4.X 数据并行》中介绍的System.Threading.Tasks.Parallel类构造的并行逻辑内部都会创建Task而它们的并行和并发执行都是由底层线程支持的。任务和线程之间也没有一对一的限制关系通用语言运行时CLR会创建必要的线程来支持任务执行的需求。 1.        Task简单的实例成员 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable {     public Task(Actionobject action, object state           , CancellationToken cancellationToken,TaskCreationOptions creationOptions);       // 获取此 Task 实例的唯一 ID。     public int Id { get; }     // 获取用于创建此任务的TaskCreationOptions。     public TaskCreationOptions CreationOptions { get; }     // 获取此任务的TaskStatus。     public TaskStatus Status { get; }     // 获取此 Task 实例是否由于被取消的原因而已完成执行。     public bool IsCanceled { get; }     // 获取 Task 是否由于未经处理异常的原因而完成。     public bool IsFaulted { get; }     // 获取导致 Task 提前结束的System.AggregateException。     public AggregateException Exception { get; }       #region IAsyncResult接口成员     private bool IAsyncResult.CompletedSynchronously { get;}     private WaitHandleIAsyncResult.AsyncWaitHandle { get; }       // 获取在创建 Task 时提供的状态对象如果未提供则为 null。     public object AsyncState { get; }     // 获取此 Task 是否已完成。     public bool IsCompleted { get; }     #endregion       // 释放由 Task 类的当前实例占用的所有资源。     public void Dispose();     …… }          分析 1)        CancellationToken、IsCancel 对于长时间运行的计算限制操作来说支持取消是一件很“棒”的事情。.NET 4.0提供了一个标准的取消操作模式。即通过使用CancellationTokenSource创建一个或多个取消标记CancellationTokencancellationToken可在线程池中线程或 Task 对象之间实现协作取消然后将此取消标记传递给应接收取消通知的任意数量的线程或Task对象。当调用CancellationToken关联的CancellationTokenSource对象的Cancle()时每个取消标记(CancellationToken)上的IsCancellationRequested属性将返回true。异步操作中可以通过检查此属性做出任何适当响应。也可调用取消标记的ThrowIfCancellationRequested()方法来抛出OperationCanceledException异常。        更多关于CancellationToken与CancellationTokenSource的介绍及示例请看《协作式取消》….        在Task任务中实现取消可以使用以下几种选项之一终止操作                                       i.              简单地从委托中返回。在许多情况下这样已足够但是采用这种方式“取消”的任务实例会转换为RanToCompletion状态而不是 Canceled 状态。                                     ii.              创建Task时传入CancellationToken标识参数并调用关联CancellationTokenSource对象的Cancel()方法 a)        如果Task还未开始那么Task实例直接转为Canceled状态。注意因为已经Canceled状态了所以不能再在后面调用Start() b)        见示例TaskOperations.Test_Cancel();如果Task已经开始在Task内部必须抛出OperationCanceledException异常注意只能存在OperationCanceledException异常可优先考虑使用CancellationToken的ThrowIfCancellationRequested()方法Task实例转为Canceled状态。 若对抛出OperationCanceledException异常且状态为Canceled的Task进行等待操作如Wait/WaitAll则会在Catch块中捕获到OperationCanceledException异常但是此异常指示Task成功取消而不是有错误的情况。因此IsCancel为trueIsFaulted为false且Exception属性为null。                                   iii.              对于使用TaskContinuationOptions枚举值为NotOn或OnlyOn创建的延续任务A在其前面的任务结束状态不匹配时延续任务A将转换为Canceled状态并且不会运行。 2)        TaskCreationOptions枚举 定义任务创建、调度和执行的一些可选行为。 None 指定应使用默认行为。 PreferFairness   较早安排的任务将更可能较早运行而较晚安排运行的任务将更可能较晚运行。(Prefer更喜欢 ; Fair公平的) LongRunning 该任务需要很长时间运行因此调度器可以对这个任务使用粗粒度的操作默认TaskScheduler为任务创建一个专用线程而不是排队让一个线程池线程来处理可通过在延续任务中访问Thread.CurrentThread.IsThreadPoolThread属性判别。比如如果任务可能需要好几秒的时间运行那么就使用这个参数。相反如果任务只需要不到1秒钟的时间运行那么就不应该使用这个参数。 AttachedToParent 指定此枚举值的Task其内部创建的Task或通过ContinueWith()创建的延续任务都为子任务。父级是顶层任务 DenyChildAttach 如果尝试附加子任务到创建的任务指定System.InvalidOperationException将被引发。 HideScheduler 创建任务的执行操作将被视为TaskScheduler.Default默认计划程序。 3)        IsCompleted Task实现了IAsyncResult接口。在任务处于以下三个最终状态之一时IsCompleted返回 trueRanToCompletion、 Faulted 或 Canceled。 4)        TaskStatus枚举 表示 Task 的生命周期中的当前阶段。一个Task实例只会完成其生命周期一次即当Task到达它的三种可能的最终状态之一时Task就结束并释放。 可能的初始状态 Created 该任务已初始化但尚未被计划。 WaitingForActivation 只有在其它依赖的任务完成之后才会得到调度的任务的初始状态。这种任务是使用定义延续的方法创建的。 WaitingToRun 该任务已被计划执行但尚未开始执行。 中间状态 Running 该任务正在运行但尚未完成。 WaitingForChildrenToComplete 该任务已完成执行正在隐式等待附加的子任务完成。 可能的最终状态 RanToCompletion 已成功完成执行的任务。 Canceled 该任务已通过对其自身的CancellationToken引发OperationCanceledException异常 Faulted 由于未处理异常的原因而完成的任务。        状态图如下                   5)        Dispose() 尽管Task为我们实现了IDisposable接口但依然不推荐你主动调用Dispose()方法而是由系统终结器进行清理。原因 a)        Task调用Dispose()主要释放的资源是WaitHandle对象。 b)        .NET4.5 对.NET4.0 中提出的Task进行过大量的优化让其尽量不再依赖WaitHandle对象eg.NET4.0种Task的WaitAll()/WaitAny()的实现依赖于WaitHandle。 c)        在使用Task时大多数情况下找不到一个好的释放点保证该Task已经完成并且没有被其他地方在使用。 d)        Task.Dispose()方法在“.NET Metro风格应用程序”框架所引用的程序集中甚至并不存在即此框架中Task没有实现IDisposable接口。 更详细更专业的Dispose()讨论请看《.NET4.X并行任务Task需要释放吗》… 2.        Task的实例方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // 获取用于等待此 Task 的等待者。 public TaskAwaiter GetAwaiter(); // 配置用于等待此System.Threading.Tasks.Task的awaiter。 // 参数:continueOnCapturedContext: //     试图在await返回时夺取原始上下文则为 true否则为 false。 public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext);   // 对提供的TaskScheduler同步运行 Task。 public void RunSynchronously(TaskScheduler scheduler); // 启动 Task并将它安排到指定的TaskScheduler中执行。 public void Start(TaskScheduler scheduler); // 等待 Task 完成执行过程。 public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken);   // 创建一个在目标 Task 完成时执行的延续任务。 public Task ContinueWith(ActionTask, object continuationAction, object state         , CancellationToken cancellationToken         , TaskContinuationOptions continuationOptions, TaskScheduler scheduler); public TaskTResultContinueWithTResult(         FuncTask, object, TResult continuationFunction         , object state,CancellationToken cancellationToken         , TaskContinuationOptions continuationOptions, TaskScheduler scheduler); ……          分析 1)        TaskContinuationOptions 在创建一个Task作为另一个Task的延续时你可以指定一个TaskContinuationOptions参数这个参数可以控制延续另一个任务的任务调度和执行的可选行为。 None 默认情况下完成前面的任务之后“都”将安排运行延续任务而不考虑前面任务的最终TaskStatus。 AttachedToParent 对延续任务指定此枚举值表示该延续任务内部创建的新Task或通过ContinueWith()创建的延续任务都为子任务。父级是延续任务 PreferFairness LongRunning DenyChildAttach            HideScheduler     参考TaskCreationOptions枚举 LazyCancellation 在延续取消的情况下防止延续的完成直到完成先前的任务。 NotOnRanToCompletion NotOnFaulted NotOnCanceled 指定不应在延续任务前面的任务“已完成运行、引发了未处理异常、已取消”的情况下安排延续任务。   此选项对多任务延续无效。 OnlyOnCanceled OnlyOnFaulted OnlyOnRanToCompletion 指定只应在延续任务前面的任务“已取消、引发了未处理异常、已完成运行”的情况下才安排延续任务。 ExecuteSynchronously 指定应同步执行延续任务。指定此选项后延续任务将在导致前面的任务转换为其最终状态的相同线程上运行。 注意 a)        如果使用默认选项TaskContinuationOptions.None并且之前的任务被取消了那么延续任务任然会被调度并启动执行。 b)        如果该条件在前面的任务准备调用延续时未得到满足则延续将直接转换为 Canceled 状态之后将无法启动。 c)        如果调用多任务延续即调用TaskFactory或TaskFactoryTResult的静态ContinueWhenAll和ContinueWhenAny方法时NotOn和OnlyOn六个标识或标识的组合都是无效的。也就是说无论先驱任务是如何完成的ContinueWhenAll和ContinueWhenAny都会执行延续任务。 d)        TaskContinuationOptions.ExecuteSynchronously指定同步执行延续任务。延续任务会使用前一个任务的数据而保持在相同线程上执行就能快速访问高速缓存中的数据从而提升性能。此外也可避免调度这个延续任务产生不必要的额外线程开销。 如果在创建延续任务时已经完成前面的任务则延续任务将在创建此延续任务的线程上运行。只应同步执行运行时间非常短的延续任务。 2)        开启任务 只有Task处于TaskStatus.Created状态时才能使用实例方法Start()。并且只有在使用Task的公共构造函数构造的Task实例才能处于TaskStatus.Created状态。 当然我们还知道有其他方式可以创建Task并开启任务比如Task.Run()/Task.ContinueWith()/Task.Factory.StartNew()/TaskCompletionSource/异步方法(即使用async与await关键字的方法)但是这些方法返回的Task已经处于开启状态即不能再调用Start()。更丰富更专业的讨论请看《.NET4.X 并行任务中Task.Start()的FAQ》… 3)        延续任务ContinueWith a)        ContinueWith() 方法可创建一个根据TaskContinuationOptions参数限制的延续任务。可以为同一个Task定义多个延续任务让它们并行执行。 比如为t1定义两个并行延续任务t2、t3. 1 2 3 Taskint t1 new Taskint(() { return 1; }); Taskint t2 t1.ContinueWithint(Work1,……); Taskint t3 t1.ContinueWithint(Work1,……); b)        调用Wait()方法和Result属性会导致线程阻塞极有可能造成线程池创建一个新线程这增大了资源的消耗并损害了伸缩性。可以在延续任务中访问这些成员并做相应操作。 c)        对前面任务的引用将以参数形式传递给延续任务的用户委托以将前面任务的数据传递到延续任务中。 4)        Wait() 一个线程调用Wait()方法时系统会检查线程要等待的Task是否已开始执行。 a)        如果是调用Wait()的线程会阻塞直到Task运行结束为止。 b)        如果Task还没有开始执行系统可能取决于局部队列的内联机制使用调用Wait()的线程来执行Task。如果发生这种情况那么调用Wait()的线程不会阻塞它会执行Task并立刻返回。                                        i.              这样做的好处在于没有线程会被阻塞所以减少了资源的使用因为不需要创建一个线程来替代被阻塞的线程并提升了性能因为不需要花时间创建一个线程也没有上下文切换。                                      ii.              但不好的地方在于假如线程在调用Wait()前已经获得一个不可重入的线程同步锁(egSpinLock)而Task试图获取同一个锁就会造成一个死锁的线程 5)        RunSynchronously 可在指定的TaskScheduler或TaskScheduler.Current中同步运行 Task。即RunSynchronously()之后的代码会阻塞到Task委托执行完毕。 示例如下 1 2 3 4 5 6 7 8 9 10 11 Task task1 new Task(() {     Thread.Sleep(5000);     Console.WriteLine(task1执行完毕。); }); task1.RunSynchronously(); Console.WriteLine(执行RunSynchronously()之后的代码。);   // 输出 // task1执行完毕。 // 执行RunSynchronously()之后的代码。 3.        Task的静态方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // 返回当前正在执行的 Task 的唯一 ID。 public static int? CurrentId{ get; } // 提供对用于创建 Task 和 TaskTResult实例的工厂方法的访问。 public static TaskFactory Factory { get; } // 创建指定结果的、成功完成的TaskTResult。 public static TaskTResult FromResultTResult(TResult result);   // 创建将在指定延迟后完成的任务。 public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken);   // 将在线程池上运行的指定工作排队并返回该工作的任务句柄。 public static Task Run(Action action, CancellationToken cancellationToken); // 将在线程池上运行的指定工作排队并返回该工作的 Task(TResult) 句柄。 public static TaskTResult  RunTResult(FuncTResult function, CancellationToken  cancellationToken); // 将在线程池上运行的指定工作排队并返回 function 返回的任务的代理项。 public static Task Run(FuncTask function, CancellationToken cancellationToken); // 将在线程池上运行的指定工作排队并返回 function 返回的 Task(TResult) 的代理项。 public static TaskTResult RunTResult(FuncTaskTResult function, CancellationToken cancellationToken);   // 等待提供的所有 Task 对象完成执行过程。 public static bool WaitAll(Task[] tasks, intmillisecondsTimeout, CancellationToken cancellationToken); // 等待提供的任何一个 Task 对象完成执行过程。 // 返回结果: //     已完成的任务在 tasks 数组参数中的索引如果发生超时则为 -1。 public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken);   // 所有提供的任务已完成时创建将完成的任务。 public static Task WhenAll(IEnumerableTask tasks); public static TaskTResult[] WhenAllTResult(IEnumerableTaskTResult tasks); // 任何一个提供的任务已完成时创建将完成的任务。 public static TaskTask WhenAny(IEnumerableTask tasks); public static TaskTaskTResult WhenAnyTResult(IEnumerableTaskTResult tasks);   // 创建awaitable等待时它异步产生当前上下文。 // 返回结果:等待时上下文将异步转换回等待时的当前上下文。 // 如果当前SynchronizationContext不为 null则将其视为当前上下文。 // 否则与当前执行任务关联的任务计划程序将视为当前上下文。 public static YieldAwaitable Yield();          分析 1)        FromResultTResult(TResult result); 创建指定结果的、成功完成的TaskTResult。我们可以使用此方法创建包含预先计算结果/缓存结果的 TaskTResult对象示例代码或CachedDownloads.cs示例文件。 2)        Delay 创建将在指定延迟后完成的任务返回Task。可以通过await或Task.Wait()来达到Thread.Sleep()的效果。尽管Task.Delay() 比Thread.Sleep()消耗更多的资源但是Task.Delay()可用于为方法返回Task类型或者根据CancellationToken取消标记动态取消等待。 Task.Delay()等待完成返回的Task状态为RanToCompletion若被取消返回的Task状态为Canceled。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var tokenSource new CancellationTokenSource(); var token tokenSource.Token; Task.Factory.StartNew(() { Thread.Sleep(1000); tokenSource.Cancel(); }); Console.WriteLine(Begin taskDelay1); Task taskDelay1 Task.Delay(100000, token); try {     taskDelay1.Wait(); } catch (AggregateException ae) {     foreach (var v in ae.InnerExceptions)         Console.WriteLine(ae.Message   v.Message); } taskDelay1.ContinueWith((t) Console.WriteLine(t.Status.ToString()));   Thread.Sleep(100); Console.WriteLine();   Console.WriteLine(Begin taskDelay2); Task taskDelay2 Task.Delay(1000); taskDelay2.ContinueWith((t) Console.WriteLine(t.Status.ToString())); // 输出 // Begin taskDelay1 // 发生一个或多个错误。已取消一个任务。 // Canceled // // Begin taskDelay2 // Completed 4.        TaskTResult:Task TaskTResult继承自Task表示一个可以返回值的异步操作提供Result只读属性用于访问异步操作的返回值。该属性会阻塞线程直到Task执行完毕并返回值。   System.Threading.Tasks.TaskFactory          1.        设置共用\默认的参数 通过TaskFactory对象提供的Scheduler、CancellationToken、CreationOption和ContinuationOptions属性可以为Task设置共用\默认的参数以便快捷的创建Task或延续任务。影响StartNew()、ContinueWhenAll()|ContinueWhenAny()、FromAsync()方法的默认参数设置。 2.        StartNew() Task.Factory.StartNew()可快速创建一个Task并且开启任务。代码如下 1 var t Task.Factory.StartNew(someDelegate); 这等效于 1 2 var t new Task(someDelegate); t.Start(); 表现方面前者更高效。Start()采用同步方式运行以确保任务对象保持一致的状态即使是同时调用多次Start()也可能只有一个调用会成功。相比之下StartNew()知道没有其他代码能同时启动任务因为在StartNew()返回之前它不会将创建的Task引用给任何人所以StartNew()不需要采用同步方式执行。更丰富更专业的讨论请看《.NET4.X 并行任务中Task.Start()的FAQ》… 3.        ContinueWhenAll() 1 2 3 public Task ContinueWhenAll(Task[] tasks, ActionTask[] continuationAction     , CancellationToken cancellationToken     , TaskContinuationOptions continuationOptions, TaskScheduler scheduler); 创建一个延续 Task 或延续 TaskTResult它将在提供的一组任务完成后马上开始。延续任务操作委托接受一个Task[]数组做参数。 4.        ContinueWhenAny() 1 2 3 public Task ContinueWhenAny(Task[] tasks, ActionTask continuationAction     , CancellationToken cancellationToken     , TaskContinuationOptions continuationOptions, TaskScheduler scheduler); 创建一个延续 Task 或延续 TaskTResult它将在提供的组中的任何一个任务完成后马上开始。延续任务操作委托接受一个 Task 做参数。 5.        通过Task.TaskFactory.FromAsync() 实例方法我们可以将APM转化为TAP。示例见此文的后面小节“AMP转化为TAP和EAP转化为TAP”。   System.Threading.Tasks.TaskScheduler                TaskScheduler表示一个处理将任务排队到线程中的底层工作对象。TaskScheduler通常有哪些应用呢 1.        TaskScheduler是抽象类可以继承它实现自己的任务调度计划。如默认调度程序ThreadPoolTaskScheduler、与SynchronizationContext.Current关联的SynchronizationContextTaskScheduler。 2.        由TaskScheduler.Default获取默认调度程序ThreadPoolTaskScheduler。 3.        由TaskScheduler.Current获取当前任务执行的TaskScheduler。 4.        由 TaskScheduler.TaskSchedulerFromCurrentSynchronizationContext() 方法获取与SynchronizationContext.Current关联的SynchronizationContextTaskSchedulerSynchronizationContextTaskScheduler上的任务都会通过SynchronizationContext.Post()在同步上下文中进行调度。通常用于实现跨线程更新控件。 5.        通过MaximumConcurrencyLevel设置任务调度计划能支持的最大并发级别。 6.        通过UnobservedTaskException事件捕获未被观察到的异常。   System.Threading.Tasks.TaskExtensions 提供一组用于处理特定类型的 Task 实例的静态方法。将特定Task实例进行解包操作。 1 2 3 4 5 public static class TaskExtensions {     public static TaskTResult UnwrapTResult(this TaskTaskTResult task);     public static Task Unwrap(this TaskTask task); }   AMP转化为TAP和EAP转化为TAP 1.        AMP转化为TAP 通过Task.TaskFactory.FromAsync() 实例方法我们可以将APM转化为TAP。 注意点 1)        FromAsync方法返回的任务具有WaitingForActivation状态并将在创建该任务后的某一时间由系统启动。如果尝试在这样的任务上调用 Start将引发异常。 2)        转化的APM异步模型必须符合两个模式 a)        接受Begin***和End***方法。此时要求Begin***方法签名的委托必须是AsyncCallback以及 End***方法只接受IAsyncResult一个参数。此模式AsyncCallback回调由系统自动生成主要工作是调用End***方法。 1 2 3 4 public TaskTResult FromAsyncTArg1, TResult(       FuncTArg1, AsyncCallback, object, IAsyncResult beginMethod     , FuncIAsyncResult, TResult endMethod, TArg1 arg1     , object state, TaskCreationOptions creationOptions); b)        接受IAsyncResult对象以及End***方法。此时Begin***方法的签名已经无关紧要只要即此模式支持传入自定义回调委托能返回IAsyncResult的参数以及 End***方法只接受IAsyncResult一个参数。 1 2 public TaskTResult FromAsyncTResult(IAsyncResult asyncResult     , FuncIAsyncResult, TResult endMethod); 3)        当然我们有时需要给客户提供统一的 Begin***() 和 End***() 调用方式我们可以直接使用Task从零开始构造APM。即在 Begin***() 创建并开启任务并返回Task。因为Task是继承自IAsyncResult接口的所以我们可以将其传递给 End***() 方法并在此方法里面调用Result属性来等待任务完成。 4)        对于返回的Task可以随时以任务延续的形式注册回调。 现在将在《APM异步编程模型》博文中展现的示例转化为TAP模式。关键代码如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public Taskint CalculateAsyncTArg1, TArg2(       FuncTArg1, TArg2, AsyncCallback, object, IAsyncResult beginMethod     , AsyncCallback userCallback, TArg1 num1, TArg2 num2, object asyncState) {     IAsyncResult result beginMethod(num1, num2, userCallback, asyncState);     return Task.Factory.FromAsyncint(result             , EndCalculate, TaskCreationOptions.None); }   public Taskint CalculateAsync(int num1, int num2, object asyncState) {     return Task.Factory.FromAsyncint, int, int(BeginCalculate, EndCalculate             , num1, num2, asyncState, TaskCreationOptions.None); } 2.        EAP转化为TAP 我们可以使用TaskCompletionSourceTResult实例将EAP操作表示为一个TaskTResult。 TaskCompletionSourceTResult表示未绑定委托的TaskTResult的制造者方并通过TaskCompletionSourceTResult.Task属性获取由此Tasks.TaskCompletionSourceTResult创建的TaskTResult。 注意TaskCompletionSourceTResult创建的任何任务将由TaskCompletionSource启动因此用户代码不应在该任务上调用 Start()方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class TaskCompletionSourceTResult {     public TaskCompletionSource();     // 使用指定的状态和选项创建一个TaskCompletionSourceTResult。     //   state: 要用作基础 TaskTResult的AsyncState的状态。     public TaskCompletionSource(object state, TaskCreationOptions creationOptions);       // 获取由此Tasks.TaskCompletionSourceTResult创建的Tasks.TaskTResult。     public TaskTResult Task { get; }       // 将基础Tasks.TaskTResult转换为Tasks.TaskStatus.Canceled状态。     public void SetCanceled();     public bool TrySetCanceled();       // 将基础Tasks.TaskTResult转换为Tasks.TaskStatus.Faulted状态。     public void SetException(Exception exception);     public void SetException(IEnumerableException exceptions);     public bool TrySetException(Exception exception);     public bool TrySetException(IEnumerableException exceptions);       // 尝试将基础Tasks.TaskTResult转换为TaskStatus.RanToCompletion状态。     public bool TrySetResult(TResult result);     ……        } 现在我将在《基于事件的异步编程模式(EAP)》博文中展现的BackgroundWorker2组件示例转化为TAP模式。 我们需要修改地方有 1)        创建一个TaskCompletionSourceint实例tcs 2)        为tcs.Task返回的任务创建延续任务延续任务中根据前面任务的IsCanceled、IsFaulted、Result等成员做逻辑 3)        Completed事件在这里面我们将设置返回任务的状态。 关键代码如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27     // 1、创建 TaskCompletionSourceTResult tcs new TaskCompletionSourceint(); worker2.RunWorkerCompleted RunWorkerCompleted;     // 2、注册延续 tcs.Task.ContinueWith(t {         if (t.IsCanceled)             MessageBox.Show(操作已被取消);         else if (t.IsFaulted)             MessageBox.Show(t.Exception.GetBaseException().Message);         else             MessageBox.Show(String.Format(操作已完成结果为{0}, t.Result));     }, TaskContinuationOptions.ExecuteSynchronously);     // 3、运行异步任务     worker2.RunWorkerAsync();     // 4、Completed事件     private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)     {         if (e.Error ! null)             tcs.SetException(e.Error);         else if (e.Cancelled)             tcs.SetCanceled();         else             tcs.SetResult((int)e.Result);         // 注销事件避免多次挂接事件         worker2.RunWorkerCompleted - RunWorkerCompleted;     }   使用关键字async和await实现异步方法        在C#5.0中引入了async和await关键字可以方便我们使用顺序结构流(即不用回调)来实现异步编程大大降低了异步编程的复杂程度。vs2010打 Visual Studio Async CTP for VS2010补丁可以引入关键字”async”和”await”的支持但是得不到.net4.5新增API的支持 异步方法的实现原理 异步方法不需要多线程因为一个异步方法并不是运行在一个独立的线程中的。 异步方法运行在当前同步上下文中只有激活的时候才占用当前线程的时间。 异步模型采用时间片轮转来实现。 异步方法的参数和返回值 异步方法的参数 不能使用“ref”参数和“out”参数但是在异步方法内部可以调用含有这些参数的方法 异步方法的返回类型 TaskTResult:Tresult为异步方法的返回值类型。 Task异步方法没有返回值。 void主要用于事件处理程序不能被等待无法捕获异常。异步事件通常被认为是一系列异步操作的开始。使用void返回类型不需要await而且调用void异步方法的函数不会捕获方法抛出的异常。异步事件中使用await倘若等待的任务由有异常会导致抛出“调用的目标发生了异常”。当然你可以在异步事件中调用另一个有返回值的异步方法 异步方法的命名规范 异步方法的方法名应该以Async作为后缀 事件处理程序基类方法和接口方法可以忽略此命名规范例如 startButton_Click不应重命名为startButton_ClickAsync     async和await关键字不会导致其他线程的创建执行异步方法的线程为其调用线程。而异步方法旨在成为非阻塞操作即当await等待任务运行时异步方法会将控制权转移给异步方法外部让其不受阻塞的继续执行待await等待的任务执行完毕再将控制权转移给await处继续执行异步方法后续的代码。 1.        我们可通过下图来明白异步方法的构建和异步方法的执行流程。代码详见我提供的示例程序async_await_method项目         需要注意的一个问题被“async”关键字标记的方法的调用都会强制转变为异步方式吗 不会当你调用一个标记了”async”关键字的方法它会在当前线程以同步的方式开始运行。所以如果你有一个同步方法它返回void并且你做的所有改变只是将其标记的“async”这个方法调用依然是同步的。返回值为Task或TaskTResult也一样。 方法用“async”关键字标记不会影响方法是同步还是异步运行并完成而是它使方法可被分割成多个片段其中一些片段可能异步运行这样这个方法可能异步完成。这些片段界限就出现在方法内部显示使用“await”关键字的位置处。所以如果在标记了“async”的方法中没有显示使用“await”那么该方法只有一个片段并且将以同步方式运行并完成。 2.        编译器转换 使用 async 关键字标记方法会导致 C# 或 Visual Basic 编译器使用状态机重新编写该方法的实现。借助此状态机编译器可以在该方法中插入多个中断点以便该方法可以在不阻止线程的情况下挂起和恢复其执行。这些中断点不会随意地插入。它们只会在您明确使用 await 关键字的位置插入 1 2 3 4 5 6 private async void btnDoWork_Click(object sender, EventArgs e) {     ...     await someObject; // -- potential method suspension point     ... } 当您等待未完成的异步操作时编译器生成的代码可确保与该方法相关的所有状态例如局部变量封装并保留在堆中。然后该函数将返回到调用程序允许在其运行的线程中执行其他任务。当所等待的异步操作在稍后完成时该方法将使用保留的状态恢复执行。 任何公开 await 模式的类型都可以进行等待。该模式主要由一个公开的 GetAwaiter()方法组成该方法会返回一个提供 IsCompleted、OnCompleted 和 GetResult 成员的类型。当您编写以下代码时 1 await someObject; 编译器会生成一个包含 MoveNext 方法的状态机类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private class FooAsyncStateMachine : IAsyncStateMachine {     // Member fields for preserving “locals” and other necessary     state     int $state;     TaskAwaiter $awaiter;     …     public void MoveNext()     {         // Jump table to get back to the right statement upon         resumption         switch (this.$state)         {             …         case 2: goto Label2;             …         }         …         // Expansion of “await someObject;”         this.$awaiter someObject.GetAwaiter();         if (!this.$awaiter.IsCompleted)         {             this.$state 2;             this.$awaiter.OnCompleted(MoveNext);             return;             Label2:         }         this.$awaiter.GetResult();         …     } } 在实例someObject上使用这些成员来检查该对象是否已完成通过 IsCompleted如果未完成则挂接一个续体通过 OnCompleted当所等待实例最终完成时系统将再次调用 MoveNext 方法完成后来自该操作的任何异常将得到传播或作为结果返回通过 GetResult并跳转至上次执行中断的位置。 3.        自定义类型支持等待 如果希望某种自定义类型支持等待我们可以选择两种主要的方法。 1)        一种方法是针对自定义的可等待类型手动实现完整的 await 模式提供一个返回自定义等待程序类型的 GetAwaiter 方法该等待程序类型知道如何处理续体和异常传播等等。 2)        第二种实施该功能的方法是将自定义类型转换为Task然后只需依靠对等待任务的内置支持来等待特殊类型。前文所展示的“EAP转化为TAP”正属于这一类关键代码如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private async void btn_Start_Click(object sender, EventArgs e) {     this.progressBar1.Value 0;       tcs new TaskCompletionSourcelt;intgt;();     worker2.RunWorkerCompleted RunWorkerCompleted;     tcs.Task.ContinueWith(t gt;     {         if (t.IsCanceled)             MessageBox.Show(操作已被取消);         else if (t.IsFaulted)             MessageBox.Show(t.Exception.GetBaseException().Message);         else             MessageBox.Show(String.Format(操作已完成结果为{0}, t.Result));     }, TaskContinuationOptions.ExecuteSynchronously);       worker2.RunWorkerAsync();     // void的异步方法主要用于事件处理程序不能被等待无法捕获异常。异步事件通常被认为     // 是一系列异步操作的开始。使用void返回类型不需要await而且调用void异步方法的函数不     // 会捕获方法抛出的异常。异步事件中使用await倘若等待的任务由有异常会导致     // 抛出“调用的目标发生了异常”。当然你可以在异步事件中调用另一个有返回值的异步方法       // 所以不需要下面的await因为会出现在执行取消后拖动界面会因异常被观察到并且终止整个进程     // await tcs.Task; }   处理TAP中的异常        在任务抛出的未处理异常都封装在System.AggregateException对象中。这个对象会存储在方法返回的Task或TaskTResult对象中需要通过访问Wait()、Result、Exception成员才能观察到异常。所以在访问Result之前应先观察IsCanceled和IsFaulted属性 1.        AggregateException对象的三个重要成员 1)        InnerExceptions属性 获取导致当前异常的System.Exception实例的只读集合即ReadOnlyCollectionException。不要将其与基类Exception提供的InnerException属性混淆。 2)        Flatten() 方法 遍历InnerExceptions异常列表若列表中包含类型为AggregateException的异常就移除所有嵌套的AggregateException直接返回其真真的异常信息效果如下图。                   1)        Handle(FuncException, bool predicate)方法 它为AggregateException中包含的每个异常都调用一个回调方法。然后回调方法可以为每个异常决定如何对其进行处理回调返回true表示异常已经处理返回false表示没有。在调用Handle之后如果至少有一个异常没有处理就创建一个新的AggregateException对象其中只包含未处理的异常并抛出这个新的AggregateException对象。 比如将任何OperationCanceledException对象都视为已处理。其他任何异常都造成抛出一个新的AggregateException其中只包含未处理的异常。 1 2 3 4 5 try{……} catch (AggregateException ae) {     ae.Handle(e e is OperationCanceledException); } 1.        父任务生成了多个子任务而多个子任务都抛出了异常 1)        嵌套子任务 1 2 3 4 5 6 7 8 9 10 Task t4 Task.Factory.StartNew(() {     Task.Factory.StartNew(() { throw new Exception(子任务Exception_1); }             , TaskCreationOptions.AttachedToParent);       Task.Factory.StartNew(() { throw new Exception(子任务Exception_2); }             , TaskCreationOptions.AttachedToParent);       throw new Exception(父任务Exception); }); 对于“嵌套子任务”中子任务的异常都会包装在父任务返回的Task或TaskTResult对象中。如此例子中 t4.Exception.InnerExceptions的Count为3。        对于子任务返回的异常类型为包装过的AggregateException对象为了避免循环访问子任务异常对象的InnerExceptions才能获取真真的异常信息可以使用上面提到的Flatten() 方法移除所有嵌套的AggregateExceprion。 2)        Continue子任务 1 2 3 4 5 6 7 8 9 10 11 12 13 Task t1 Task.Factory.StartNew(() {     Thread.Sleep(500);   // 确保已注册好延续任务     throw new Exception(父任务Exception); }, TaskCreationOptions.AttachedToParent); Task t2 t1.ContinueWith((t) {     throw new Exception(子任务Exception_1); }); Task t3 t1.ContinueWith((t) {     throw new Exception(子任务Exception_2); });        对于“Continue子任务”中的子任务其异常与父任务是分离的各自包装在自己返回的Task或 TaskTResult对象中。如此示例 t1、t2、t3 的Exception.InnerExceptions的Count都为1。     2.        TaskScheduler的UnobservedTaskException事件 假如你一直不访问Task的Wait()、Result、Exception成员那么你将永远注意不到这些异常的发生。为了帮助你检测到这些未处理的异常可以向TaskScheduler对象的UnobservedTaskException事件注册回调函数。每当一个Task被垃圾回收时如果存在一个没有注意到的异常CLR的终结器线程会引发这个事件。 可在事件回调函数中调用UnobservedTaskExceptionEventArgs对象的SetObserved() 方法来指出已经处理好了异常从而阻止CLR终止线程。然而并不推荐这么做宁愿终止进程也不要带着已经损坏的状态继续运行。 示例代码要监控此代码必须在GC.Collect();和事件里两个地方进行断点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 TaskScheduler.UnobservedTaskException (s, e) {     //设置所有未觉察异常已被处理     e.SetObserved(); }; Task.Factory.StartNew(() {     throw new Exception(); }); //确保任务完成 Thread.Sleep(100); //强制垃圾会受到在GC回收时才会触发UnobservedTaskException事件 GC.Collect(); //等待终结器处理 GC.WaitForPendingFinalizers(); 3.        返回void的async“异步方法”中的异常 我们已经知道返回Task或TaskTResult对象的任务中抛出的异常会随着返回对象一起返回可通过Exception属性获取。这对于返回Task或TaskTResult对象的“异步方法”情况也是一样。 然而对于返回void的“异步方法”方法中抛出的异常会直接导致程序奔溃。 1 2 3 4 public static async void Test_void_async_Exception() {     throw new Exception(); } 另外我们还要特别注意lambda表达式构成的“异步方法”如 1 Enumerable.Range(0, 3).ToList().ForEach(async (i) { throw new Exception(); });            本博文到此结束我相信你看累了其实我也写了很久…很久…写完此文我的“异步编程系列”也算有头有尾了还会继续扩充。本博文主要介绍了Task的重要API、任务的CLR线程池引擎、TaskFactory对象、TaskScheduler对象、TaskExtensions对象、AMP转化为TAP和EAP转化为TAP、使用关键字async和await实现异步方法以及自定义类型支持等待、处理TAP中的异常。 感谢你的观看如果对你有帮助还请多多推荐……     本篇博文基于.NET4.5中TPL所写。对于.NET4.0中TPL会有些差异若有园友知道差异还请告知我这边做个记录方便大家也方便自己。 1、.NET4.0中TPL未观察到的异常会在GC回收时终止进程。园友YamatAmain讨论见21-26楼       推荐阅读          异步性能了解 Async 和 Await 的成本-----有讲解到使用Task.ConfigureAwait(false)来避免捕获原上下文来提升性能。 关于async与await的FAQ      -----详细讲解了await和async的作用和意义以及什么是可等待对象、等待者……此文可帮助你解决80%关于await和async关键字的疑惑                深入探究 WinRT 和 await      -----基于WinRT平板win8系统讲解了异步功能API、TAP、编译器转换……     参考资料MSDN                     书籍《CLR via C#(第三版)》 书籍《C# 并行编程高级教程精通.NET 4 Parallel Extensions》 作者滴答的雨 出处http://www.cnblogs.com/heyuquan/ 本文版权归作者和博客园共有欢迎转载但未经作者同意必须保留此段声明且在文章页面明显位置给出原文连接否则保留追究法律责任的权利。
http://www.pierceye.com/news/334286/

相关文章:

  • 北京社保网站做社保增减员锦绣大地seo官网
  • 分析影响网站排名的因素河南省住房和城乡建设厅网站文件
  • 宁城网站建设公司引流最好的推广方法
  • 辽宁省建设厅官方网站网站免费正能量直接进入浏览器下载安装
  • 怎么给公司建网站广州互联网营销师培训
  • 用阿里云做网站注意事项绵阳的网站建设公司哪家好
  • 电商网站设计工作内容深圳国际设计学院
  • 国内界面优秀的网站科技有限公司名字叫什么好
  • 网站底部悬浮代码搭建网站的主要风险
  • 长安网站建设公司常做网站首页的文件名
  • 学网站开发的能找什么工作赣州网站设计较好的公司
  • 网站建设接单微信营销软件收费排行榜
  • 佛山网站建设公司排名佛山微网站推广哪家专业
  • 招商网站建设网设备 光速东莞网站建设
  • 网站建设公司如何wordpress用多大主机
  • 东莞网站建设规范网页美工设计(第2版)素材
  • 论文 网站建设值得推荐的深圳app外包公司
  • 建网站的电脑可以换位置吗莆田建站培训
  • 外贸必看网站离职模板网
  • 内网网站建设正能量不良网站软件下载
  • 制作手机广告的网站吉林省建设厅网站评职称系统
  • 云南建设厅网站资质证书查询自动生成app
  • 柳州正规网站制作公司大连建设厅网站
  • 北京市保障房建设投资中心网站瘫痪广州大型网站建设公司排名
  • 做电池网站的引导页室内设计联盟效果图
  • 查询备案网站成绩查询系统网站开发
  • 网站后台编辑器上传不了图片建筑工程承包网app
  • wordpress多站点插件168工程信息网
  • 网站工信部备案号没有ftp wordpress
  • 家装公司网站建设网站建立网站有免费的吗