原因和简单,ReflectedAsyncActionDescriptor的BeginExecute方法在执行XxxAsync方法的前后会分别调用AsyncManager的OutstandingOperations属性的Increment和Decrement方法。对于我们给出的例子来说,在执行ArticleAsync之前Increment方法被调用使计算器的值变成1,随后ArticleAsync被执行,由于该方法以异步的方式读取指定的文件内容,所以会立即返回。最后Decrement方法被执行使计数器的值变成0,AsyncManager的Completed事件被触发并导致ArticleCompleted方法的执行。而此时,文件内容的读取正在进行之中,表示文章内容的content参数自然尚未被初始化。
ReflectedAsyncActionDescriptor这样的执行机制也对我们使用AsyncManager提出了要求,那就是对尚未完成的一步操作计数器的增加操作不应该发生在异步线程中,如下所示的针对AsyncManager的OutstandingOperations属性的Increment方法的定义是不对的。
public class HomeController AsyncController { public void XxxAsync(string name) { Task.Factory.StartNew(() => { AsyncManager.OutstandingOperations.Increment(); //... AsyncManager.OutstandingOperations.Decrement(); }); } //其他成员 }
下面采用正确的定义方法:
public class HomeController AsyncController { public void XxxAsync(string name) { AsyncManager.OutstandingOperations.Increment(); Task.Factory.StartNew(() => { //... AsyncManager.OutstandingOperations.Decrement(); }); } //其他成员 }
最后再强调一点,不论是显式调用AsyncManager的Finish方法,还是通过调用AsyncManager的OutstandingOperations属性的Increment方法是计数器的值变成零,仅仅是让XxxCompleted方法得以执行,并不能真正阻止异步操作的执行。
五、异步操作的超时控制
异步操作虽然适合那些相对耗时的I/O绑定型操作,但是也并不说对一步操作执行的时间没有限制。异步超时时限通过AsyncManager的整型属性Timeout表示,它表示超时时限的总毫秒数,其默认值为45000(45秒)。如果将Timeout属性设置为-1,意味着异步操作执行不再具有任何时间的限制。对于以XxxAsync/XxxCompleted形式定义的异步Action来说,如果XxxAsync执行之后,在规定的超时时限中XxxCompleted没有得到执行,一个TimeoutException会被抛出来。
如果我们以返回类型为Task的形式定义异步Action,通过Task体现的异步操作的执行时间不受AsyncManager的Timeout属性的限制。我们通过如下的代码定义了一个名为Data的异步Action方法以异步的方式获取作为Model的数据并通过默认的View呈现出来,但是异步操作中具有一个无限循环,当我们访问该Data方法时,异步操作将会无限制地执行下去,也不会有TimeoutException异常发生。
public class HomeController AsyncController { public Task<ActionResult> Data() { return Task.Factory.StartNew(() => { while (true) { } return GetModel(); }).ContinueWith<ActionResult>(task => { object model = task.Result; return View(task.Result); }); } //其他成员 }
在ASP.NET MVC应用编程接口中具有两个特殊的特性用于定制异步操作执行的超时时限,它们是具有如下定义的AsyncTimeoutAttribute和NoAsyncTimeoutAttribute,均定义在命名空间System.Web.Mvc下。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=false)] public class AsyncTimeoutAttribute ActionFilterAttribute { public AsyncTimeoutAttribute(int duration); public override void OnActionExecuting(ActionExecutingContext filterContext); public int Duration { get; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=false)] public sealed class NoAsyncTimeoutAttribute AsyncTimeoutAttribute { // Methods public NoAsyncTimeoutAttribute() base(-1) { } }
从上面给出的定义我们可以看出这两个特性均是ActionFilter。AsyncTimeoutAttribute的构造函数接受一个表示超时时限(以毫秒为单位)的整数作为其参数,它通过重写OnActionExecuting方法将指定的超时时限设置给当前Controller的AsyncManager的Timeout属性进行。NoAsyncTimeoutAttribute是AsyncTimeoutAttribute的继承者,它将超时时限设置为-1,意味着它解除了对超时的限制。
从应用在这两个特性的AttributeUsageAttribute定义可看出,它们既可以应用于类也可以用于也方法,意味着我们可以将它们应用到Controller类型或者异步Action方法(仅对XxxAsync方法有效,不能应用到XxxCompleted方法上)。如果我们将它们同时应用到Controller类和Action方法上,针对方法级别的特性无疑具有更高的优先级。