详解ASP.NET MVC下的异步Action的定义和执行原理

2019-05-26 01:31:58于丽

也就是说针对当前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是如何被执行的呢?

原因和简单,ReflectedAsyncActionDescriptor的BeginExecute方法在执行XxxAsync方法的前后会分别调用AsyncManager的OutstandingOperations属性的Increment和Decrement方法。对于我们给出的例子来说,在执行ArticleAsync之前Increment方法被调用使计算器的值变成1,随后ArticleAsync被执行,由于该方法以异步的方式读取指定的文件内容,所以会立即返回。最后Decrement方法被执行使计数器的值变成0,AsyncManager的Completed事件被触发并导致ArticleCompleted方法的执行。而此时,文件内容的读取正在进行之中,表示文章内容的content参数自然尚未被初始化。