简体   繁体   English

ASP.NET异步任务执行序列

[英]ASP.NET async task execution sequence

I just come across concurrency coding on asp.net and found there is 2 ways to trigger an async method at Page_Load method 我只是在asp.net上遇到并发编码,发现在Page_Load方法中有两种触发异步方法的方法

  1. RegisterAsyncTask(new PageAsyncTask(DoSthAsync())); RegisterAsyncTask(new PageAsyncTask(DoSthAsync()));
  2. await DoSthAsync(); 等待DoSthAsync();

However, they have different operation result. 但是,它们具有不同的运算结果。 For the case 1, the code right after RegisterAsyncTask will run immediately before any code in DoSthAsync(). 对于情况1, RegisterAsyncTask之后的代码将立即在DoSthAsync()中的任何代码之前运行。 While the code after await will run at completion of DoSthAsync(). 等待后的代码将在DoSthAsync()完成时运行。

Eg: 例如:

//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();
}

This code snippet will generate the following result: 此代码段将产生以下结果:

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

While I uncomment await DoSthAsync() code and comment RegisterAsyncTask , the following result will be shown. 当我取消注释等待DoSthAsync()代码并注释RegisterAsyncTask时 ,将显示以下结果。

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

In Rick Anderson's article Using Asynchronous Methods in ASP.NET 4.5 , he suggested using RegisterAsyncTask will give a better control on code execution. 在Rick Anderson的文章“ 在ASP.NET 4.5中使用异步方法”中 ,他建议使用RegisterAsyncTask可以更好地控制代码执行。 However, this gives an unexpected result which I'm looking for while await in page_load will generate identical one as I try the similar code sequence in windows program. 但是,这给出了意外的结果,我在page_load中等待时正在寻找的结果将与在Windows程序中尝试类似的代码序列时生成相同的结果。

In his article, Rick also has code of Stopwatch start before GetPWGsrvAsync() fired and stop after all async code completed showing how long the code executed. 在他的文章中,Rick还具有Stopwatch的代码,该代码在GetPWGsrvAsync()触发之前开始,在所有异步代码完成后停止,以显示代码执行了多长时间后停止。 It shows elapsed time is 0.872s in the screen cap. 它显示的屏幕时间为0.872s。 Therefore it's expected stopwatch is stopped after all previous code including all in async method completed. 因此,预期秒表将在所有先前的代码(包括异步方法中的所有代码)完成后停止。

.......
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();
.......

While I follow it the same way like the code snippet above, I have different result in either RegisterAsyncTask or await DoSthAsync() . 尽管我像上面的代码片段一样使用它,但是在RegisterAsyncTaskawait DoSthAsync()中却得到了不同的结果。 Although the Elapsed time displayed at different position, they both give me very short elapsed time about 0.010s or 10+ms and it's believe the stopwatch is stopped very shortly after async function DoSthAsync() is fired. 尽管经过时间显示在不同的位置,但它们都给我很短的经过时间,大约为0.010s或10 + ms,并且可以相信,在触发异步函数DoSthAsync()之后,秒表很快就停止了。 In fact, similar result in window program too, it stopped very soon. 实际上,在窗口程序中也有类似的结果,它很快就停止了。

After long description of the problem, I would like to ask which way of async coding to have better control and code pattern. 详细描述问题之后,我想问一下异步编码的哪种方式具有更好的控制和代码模式。 In between, how can I have expected result of stopwatch to give me exact code elapsed time. 在两者之间,我如何期望秒表的结果给我准确的代码经过时间。

Page async tasks have been around since long before async-await . 页面异步任务早在async-await之前就已经存在。

Page async tasks are about executing asynchronous tasks (not necessarily Task s) in the request lifecycle. 页面异步任务是关于在请求生命周期中执行异步任务(不一定是Task )。

async-await is about having asynchronous methods with asynchronous operations. async-await是关于具有异步方法和异步操作的。

If you use async-await in Page_Load , because it is a void -returning method, the runtime has no way to know that the method is asynchronous and if its asynchronous work is done or not. 如果在Page_Load使用async-await ,因为它是一个返回void方法,那么运行时将无法知道该方法是异步的,以及其异步工作是否完成。

Have a look at the documentation for PageAsyncTask and see what best suits your needs. 看一下PageAsyncTask的文档 ,看看最适合您的需求。 But you should have a serious look at page async tasks. 但是您应该认真看一下页面异步任务。

First things first: your Stopwatch returns such ridiculously short amounts of time in both cases since you use the wrong property. 首先, Stopwatch在两种情况下都返回如此短的时间,因为您使用了错误的属性。 stopWatch.Elapsed.Milliseconds will just return you the amount of milliseconds in the last second of the measured intervall. stopWatch.Elapsed.Milliseconds只会向您返回所测量间隔的最后一秒中的毫秒数。 What you wanted is stopWatch.ElapsedMilliseconds since this would return the total amount of milliseconds which elapsed during the measurement. 您想要的是stopWatch.ElapsedMilliseconds因为这将返回测量期间经过的毫秒总数。 And this will provide you with great differences between the two used methods. 这将为您提供两种使用方法之间的巨大差异。

The await DoSthAsync(); await DoSthAsync(); will have an execution time of a little bit above one second. 执行时间将超过一秒。 This is due to the fact that the await keyword basically means: wait until the asynchronous operation is finished successful and only afterwards execute the code below. 这是由于await关键字的基本含义是:等待异步操作成功完成,然后再执行下面的代码。 This guarantees you that at least in this context the code is run in a synchronous style but the execution of the long running operation is scheduled to a ThreadPool thread. 这样可以确保您至少在这种情况下以同步方式运行代码,但是将长期运行的操作安排在ThreadPool线程中。 This in fact does what you want but still has the downside of using async/await in a void returning method which is not illegal but I'd refrain from it. 实际上,这确实可以满足您的要求,但是在void返回方法中仍然有使用async / await的缺点,这不是非法的,但我会避免这样做。 You can read everything on the downsides of it here . 您可以在此处阅读其缺点的所有内容。

The second approach which uses RegisterAsyncTask and PageAsyncTask will have an execution time somewhere below 1 second. 使用RegisterAsyncTaskPageAsyncTask的第二种方法的执行时间将小于1秒。 This is due to the fact that it simply registers the task for execution and immediately returns to process the code below. 这是因为它只注册要执行的任务,然后立即返回以处理下面的代码。 Since your code is written in the Page_Load event the execution of your long running operation will only start automatically after the PreRenderComplete event has completed. 由于您的代码是在Page_Load事件中编写的,因此长时间运行的操作将仅在PreRenderComplete事件完成后自动开始。 But you can start the execution of your task manually with Page.ExecuteRegisteredAsyncTasks(); 但是您可以使用Page.ExecuteRegisteredAsyncTasks();手动开始执行任务Page.ExecuteRegisteredAsyncTasks(); . But note: this also won't wait for the completion of your task and just start the execution earlier. 但请注意:这也不会等待任务完成,而只会更早开始执行。

Basically you have two options left to obtain the result that you desire: 基本上,您还有两个选择来获得所需的结果:

  1. Use your first approach and live with the downsides. 使用您的第一种方法并忍受不利之处。
  2. Refactor your code so that you can use the second approach by using another constructor of the PageAsyncTask which embraces EventHandlers. 重构代码,以便可以通过使用包含EventHandlers的PageAsyncTask的另一个构造函数来使用第二种方法。 You can use those EventHandlers to execute the code which should run after the asynchronous task has finished. 您可以使用这些EventHandlers执行应在异步任务完成后运行的代码。 You can find a good example for this approach here . 您可以在这里找到这种方法的一个很好的例子。

What you will do with this information is up to you. 您将如何处理此信息。 Just be reminded that I'd highly recommend using option 2 to you. 请注意,我强烈建议您使用选项2。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM