简体   繁体   中英

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

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

However, they have different operation result. For the case 1, the code right after RegisterAsyncTask will run immediately before any code in DoSthAsync(). While the code after await will run at completion of 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.

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. 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.

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. It shows elapsed time is 0.872s in the screen cap. 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() . 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. 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 .

Page async tasks are about executing asynchronous tasks (not necessarily Task s) in the request lifecycle.

async-await is about having asynchronous methods with asynchronous operations.

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.

Have a look at the documentation for PageAsyncTask and see what best suits your needs. 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.Elapsed.Milliseconds will just return you the amount of milliseconds in the last second of the measured intervall. What you wanted is stopWatch.ElapsedMilliseconds since this would return the total amount of milliseconds which elapsed during the measurement. And this will provide you with great differences between the two used methods.

The 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. 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. 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. 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. 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. But you can start the execution of your task manually with 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. You can use those EventHandlers to execute the code which should run after the asynchronous task has finished. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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