AsyncManager.OutstandingOperations.Increment(3); Task.Factory.StartNew(() => { //异步操作1 AsyncManager.OutstandingOperations.Decrement(); }); Task.Factory.StartNew(() => { //异步操作2 AsyncManager.OutstandingOperations.Decrement(); }); Task.Factory.StartNew(() => { //异步操作3 AsyncManager.OutstandingOperations.Decrement(); });
对于每次通过Increment和Decrement方法调用引起的计数数值的改变,OperationCounter对象都会检验当前计数数值是否为零,如果则表明所有的操作运行完毕,如果预先注册了Completed事件,该事件会被触发。值得一提的时候,表明所有操作完成执行的标志是计数器的值等于零,而不是小于零,如果我们通过调用Increment和Decrement方法使计数器的值称为一个负数,注册的Completed事件是不会被触发的。
AsyncManager在初始化的时候就注册了通过属性OutstandingOperations表示的OperationCounter对象的Completed事件,使该事件触发的时候调用自身的Finish方法。而虚方法Finish在AsyncManager中的默认实现又会触发自身的Finished事件。
如下面的代码片断所示,Controller类实现了IAsyncManagerContainer接口,而后者定义了一个只读属性AsyncManager用于提供辅助执行异步Action的AsyncManager对象,而我们在定义异步Action方法是使用的AsyncManager对象就是从抽象类Controller中集成下来的AsyncManager属性。
public abstract class Controller ControllerBase, IAsyncManagerContainer,... { public AsyncManager AsyncManager { get; } } public interface IAsyncManagerContainer { AsyncManager AsyncManager { get; } }
四、Completed方法的执行
对于通过XxxAsync/XxxCompleted形式定义的异步Action,我们说回调操作XxxCompleted会在定义在XxxAsync方法中的异步操作执行结束之后被自动调用,那么XxxCompleted方法具体是如何被执行的呢?
异步Action的执行最终是通过描述该Action的AsyncActionDescriptor对象的BeginExecute/EndExecute方法来完成的。通过之前“Model的绑定”的介绍我们知道通过XxxAsync/XxxCompleted形式定义的异步Action通过一个ReflectedAsyncActionDescriptor对象来表示的,ReflectedAsyncActionDescriptor在执行BeginExecute方法的时候会注册Controller对象的AsyncManager的Finished事件,使该事件触发的时候去执行Completed方法。
也就是说针对当前Controller的AsyncManager的Finished事件的触发标志着异步操作的结束,而此时匹配的Completed方法会被执行。由于AsyncManager的Finish方法会主动触发该事件,所以我们可以通过调用该方法使Completed方法立即执行。由于AsyncManager的OperationCounter对象的Completed事件触发的时候会调用Finish方法,所以当表示当前正在执行的异步操作计算器的值为零时,Completed方法也会自动被执行。
如果我们在XxxAsync方法中通过如下的方式同时执行三个异步操作,并在每个操作完成之后调用AsyncManager的Finish方法,意味着最先完成的异步操作会导致XxxCompleted方法的执行。换句话说,当XxxCompleted方法执行的时候,可能还有两个异步操作正在执行。
AsyncManager.OutstandingOperations.Increment(3); Task.Factory.StartNew(() => { //异步操作1 AsyncManager.Finish(); }); Task.Factory.StartNew(() => { //异步操作2 AsyncManager.Finish(); }); Task.Factory.StartNew(() => { //异步操作3 AsyncManager.Finish(); });
如果完全通过为完成的异步操作计数机制来控制XxxCompleted方法的执行,由于计数的检测和Completed事件的触发只发生在OperationCounter的Increment/Decrement方法被执行的时候,如果我们在开始和结束异步操作的时候都没有调用这两个方法,XxxCompleted是否会执行呢?同样以之前定义的用语读取/显示文章内容的异步Action为例,我们按照如下的方式将定义在ArticleAsync方法中针对AsyncManager的OutstandingOperations属性的Increment和Decrement方法调用注释调用,ArticleCompleted方法是否还能正常运行呢?
public class HomeController AsyncController { public void ArticleAsync(string name) { //AsyncManager.OutstandingOperations.Increment(); Task.Factory.StartNew(() => { string path = ControllerContext.HttpContext.Server.MapPath(string.Format(@"\articles\{0}.html", name)); using (StreamReader reader = new StreamReader(path)) { AsyncManager.Parameters["content"] = reader.ReadToEnd(); } //AsyncManager.OutstandingOperations.Decrement(); }); } public ActionResult ArticleCompleted(string content) { return Content(content); } }
实际上ArticleCompleted依然会被执行,但是这样我们就不能确保正常读取文章内容,因为ArticleCompleted方法会在ArticleAsync方法执行之后被立即执行。如果文章内容读取是一个相对耗时的操作,表示文章内容的ArticleCompleted方法的content参数在执行的时候尚未被初始化。在这种情况下的ArticleCompleted是如何被执行的呢?