ASP.NET异步任务执行序列

时间:2016-03-07 06:28:41

标签: c# asp.net concurrency async-await task-parallel-library

我刚刚在asp.net上遇到并发编码,发现有两种方法可以在Page_Load方法中触发异步方法

  1. RegisterAsyncTask(new PageAsyncTask(DoSthAsync()));
  2. 等待DoSthAsync();
  3. 但是,它们的操作结果不同。对于案例1, RegisterAsyncTask 之后的代码将紧接在DoSthAsync()中的任何代码之前运行。虽然等待之后的代码将在DoSthAsync()完成时运行。

    例如:

    //protected async void Page_Load(object sender, EventArgs e)
    protected void Page_Load(object sender, EventArgs e)
    {    
        Response.Write("Start</br>");
        Response.Flush();
        RegisterAsyncTask(new PageAsyncTask(DoSthAsync()));
        //await DoSthAsync();
        Response.Write("End</br>");
        Response.Flush();
    }
    
    public async Task LoadSomeData()
    {
        await Task.Delay(1000);
        Response.Write("Do Sth Async</br>");
        Response.Flush();
    }
    

    此代码段将生成以下结果:

    Start
    End
    Do Sth Async  *(after 1 second delay)*
    

    当我取消注释等待DoSthAsync()代码并注释 RegisterAsyncTask 时,将显示以下结果。

    Start
    Do Sth Async  *(after 1 second delay)*
    End
    

    在Rick Anderson的文章Using Asynchronous Methods in ASP.NET 4.5中,他建议使用 RegisterAsyncTask 可以更好地控制代码执行。但是,这给出了一个意想不到的结果,我正在寻找,而在Windows程序中尝试类似的代码序列时,page_load中的await将生成相同的结果。

    在他的文章中,Rick还在GetPWGsrvAsync()之前启动了秒表启动代码,并在所有异步代码完成后停止显示代码执行了多长时间。它显示屏幕截图中的经过时间为0.872秒。因此,在所有先前的代码(包括所有异步方法完成)之后,它的预期秒表会停止。

    .......
    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();
    RegisterAsyncTask(new PageAsyncTask(DoSthAsync()));
    //await DoSthAsync();
    stopWatch.Stop();
    Response.Write(String.Format("Elapsed time:{0}",stopWatch.Elapsed.Milliseconds / 1000.0));
    Response.Write("</br>");
    Response.Flush();
    .......
    

    虽然我像上面的代码片段一样遵循它,但我在 RegisterAsyncTask await DoSthAsync()中有不同的结果。虽然经过的时间显示在不同的位置,但它们都给我很短的经过时间大约0.010秒或10 + ms并且它相信秒表在异步功能后很快停止触发了DoSthAsync()。事实上,在窗口程序中也有类似的结果,它很快停止了。

    在对问题进行了长时间的描述之后,我想问一下异步编码的哪种方式可以获得更好的控制和代码模式。在这两者之间,我怎么能有预期的秒表结果给我确切的代码经过时间。

2 个答案:

答案 0 :(得分:1)

页面异步任务早在async-await之前就已存在。

页面异步任务是关于在请求生命周期中执行异步任务(不一定是Task)。

async-await是关于使用异步操作的异步方法。

如果在async-await中使用Page_Load,因为它是一个void - 返回方法,运行时无法知道该方法是异步的,以及它的异步工作是否完成或不。

查看the documentation for PageAsyncTask,了解最适合您需求的内容。但是你应该认真看看页面异步任务。

答案 1 :(得分:1)

首先要做的事情是:由于你使用了错误的属性,你的Stopwatch在这两种情况下都会返回如此荒谬的短暂时间。 stopWatch.Elapsed.Milliseconds只会返回测量的intervall最后一秒的毫秒数。您想要的是stopWatch.ElapsedMilliseconds,因为这将返回测量期间经过的总毫秒数。这将为您提供两种使用方法之间的巨大差异。

await DoSthAsync();的执行时间会略高于一秒。这是因为await关键字基本上意味着:等到异步操作成功完成后才执行下面的代码。这可以保证至少在此上下文中代码以同步样式运行,但是长时间运行操作的执行被调度到ThreadPool线程。这实际上做了你想要的,但仍然有一个在void返回方法中使用async / await的缺点,这不是非法的,但我不会这样做。你可以阅读它的缺点here

使用RegisterAsyncTaskPageAsyncTask的第二种方法的执行时间低于1秒。这是因为它只是将任务注册为执行并立即返回以处理下面的代码。由于您的代码是在Page_Load事件中编写的,因此长时间运行操作的执行只会在PreRenderComplete事件完成后自动启动。但您可以使用Page.ExecuteRegisteredAsyncTasks();手动开始执行任务。但请注意:这也不会等待任务完成,只是提前开始执行。

基本上你还有两个选择来获得你想要的结果:

  1. 使用您的第一种方法并承受缺点。
  2. 重构您的代码,以便您可以使用包含EventHandlers的PageAsyncTask的另一个构造函数来使用第二种方法。您可以使用这些EventHandler来执行应在异步任务完成后运行的代码。您可以找到此方法的一个很好的示例here
  3. 您将对此信息做些什么取决于您。请注意,我强烈建议您使用选项2。