Explain async await again

前端 未结 4 1924
南方客
南方客 2020-12-06 15:48

This is my event handler code:

protected async void TestrunSaveExecute()
{
    bool saveResult = await SaveTestRunAsync();
}

In order to ke

相关标签:
4条回答
  • 2020-12-06 16:19

    In my understanding, I can now do some lengthy operations in SaveTestRunAsync() without blocking the UI, since it is decoupled by using the await keyword.

    async/await doesn't mean you can do "lengthy operations" without blocking the UI. On the contrary, when you use await, you aren't internally queueing up work on a different thread, your code continues to execute on the same thread (in your case its the UI thread), until hitting the inner await and returning to the caller.

    The difference between Thread.Sleep and Task.Delay is that the former is a blocking call, it will halt your UI thread until the time specified is over. The latter, will internally use a Timer and yield control back to the calling method. Once that timer's clock elapses, it will resume execution where it left off (this is where compiler magic comes in)

    0 讨论(0)
  • 2020-12-06 16:19

    Async/await is a higher level abstraction than threads.

    The actual implementation may use a worker thread to implement async, but it may (and often does) use other synchronization techniques within same thread.

    So a call to Thread.Sleep will block the UI thread, if the current implementation of async/await is not thread-based.

    Related reading: It's All About the SynchronizationContext .

    0 讨论(0)
  • 2020-12-06 16:20

    The code is still running on the UI thread.

    It is not running on a background thread.

    As such, any lengthy, costly, operation you're doing in that async method will still block the UI for that period of time.

    Thread.Sleep puts the UI thread to sleep.

    You need to understand how async and await works in this case.

    await here basically says this:

    Let's split the method in two here. The first portion is whatever executes up to the point of the await. The second portion is whatever should execute after the awaitable object has completed.

    So, basically, the method executes up until it reaches await Task.Delay(5000);. Then it puts a "delay of 5 seconds" into play, and says "schedule the rest of the code to execute when that is done". Then it returns so that the UI thread can keep pumping messages.

    Once the 5 seconds is up, the UI thread executes the rest of that method.

    Basically, this is good if you do asynchronous I/O, but not so good if you do costly operations, like processing large datasets or similar.

    So how then can you do that?

    You can use Task.Run.

    This will spin up another thread to execute the delegate it is given, and the UI thread is free to do other stuff in the meantime.

    Basically you can think of a method using await as this:

    Life of method:    <------------------------------------------------------->
    Parts:             [ start of method ----][awaitable][ rest of method -----]
    

    So the method will execute the first portion, until it reaches await X, then it will check if X is already done, if it's not done, it will set up some Task objects in such a way that the awaitable object gets to run, and once it has completed, the "rest of the method" gets to run.

    If X was already done, perhaps it's an asynchronous I/O operation that has already completed, or it completed really fast, then the method will keep executing the rest of the method as though you hadn't written await there.

    But if not, then it returns. This is important, because it lets the UI thread (in this case) get back to pumping messages like mouse clicks and things from the user.

    Once the task waiting for that "awaitable object" is told that the awaitable is completed, the "rest of the method" is scheduled, which basically (in this case) puts a message into the message queue asking it to execute the rest of that method.

    The more await statements you have in such a method, the more pieces, basically it will just split the method up into more parts.

    You can do all of this with the Task class that was introduced in an earlier .NET version. The whole purpose of async / await is to make it easier to write code, since wrapping up your code in task objects had the unfortunate effect of turning your code inside out, and making it hard to handle things like exceptions and loops.

    0 讨论(0)
  • 2020-12-06 16:22

    Await does not create a thread or a task, it "simply" calls the awaited function (which is not supposed to block, otherwise the caller will be blocked as this is still synchronous), asks the compiler to generate a continuation for the task returned by the awaited function, containing the "rest" of the code after the await, and returns a new task to the caller (if the awaiting function returns a task). That task will be signalled as complete when the continuation completes.

    The framework makes sure the continuation will run on the UI thread (if the application has one), which is a must if you want to be able to access UI elements from the asynchonous functions.

    So an asynchronous function should not contain a blocking call. Blocking calls should be "wrapped" in a task using Task.Run()

    0 讨论(0)
提交回复
热议问题