How ConfigureAwait(false) Prevent Ui Deadlocks

对着背影说爱祢 提交于 2021-02-08 06:31:12

问题


I Know that this question has been asked endlessly, but still. I think i am missing somthing. When we want updating UI( WinForm UI for the sake of argument ) in asynchronously manner. But we cannot using the async /await keyword and we have to use the ConfigureAwait(false). As i understand it "Tells" the task that it can resume itself on any available thread instead of waiting to to the main thread. Its preventing the deadlock by free the UI for not waiting for the long process to be completed and task is not waiting for the main UI thread to be available.

The following code demonstrate the classical deadlock

public void Button_Click()
{

    SomeTextBox.Text =LongProcessAsync().Result;
}

So now my question start :). How eventually UI thread update UI after long proccess task has completed.

Is it because task passing the result to another UI process to complete the job ? How UI queue message related to that parts ? When saying only one thread updating the ui , does it mean that this thread live for the all life cycle of the application or only one thread is updating ui but a new thread can be created and do the stuff ?

Thanks


回答1:


I recommend reading Stephen Cleary's blog post on this topic: Don't Block on Async Code which explains how the deadlock happen and how to avoid it.

Pay attention to the difference of "deadlock" and "blocked UI". Deadlock happens when two threads are waiting for each other, Blocked UI happens when UI thread is busy/blocked and cannot process UI messages. Here using ConfigureAwait(false) doesn't prevent "blocked UI", but prevents "deadlock".

When you write an async library methods which runs a task, to prevent a possible deadlock it's recommended to run your task by ConfigureAwait(false). It's to prevent deadlock even in case the users of your library get the result of your async method by calling Result or Wait.

ConfigureAwait(false) basically tells: not to come back to the original context after this line and continue execution on thread pool thread.

To understand it better, look at this example:

// UI code 
1:  private void button1_Click(object sender, EventArgs e) 
2:  { 
3:      var result = GetDataAsync().Result; 
4:      MessageBox.Show(result); 
5:  } 

// A library code
6:  public async Task<string> GetDataAsync() 
7:  { 
8:      await Task.Delay(1000); 
9:      return "Data"; 
10: } 

Considering the following facts:

  • In Windows Forms, all UI code executes in a single thread; the UI thread.

  • Result method of the Task blocks the calling thread until the result of the task is ready.

This is what happens here:

  • At line 3, GetDataAsync will be called and executed in UI thread up to line 8.
  • At line 8 a task will be created and will run on thread pool thread and await is telling after running that task, the execution should continue in the previously captured context (Which is UI thread context).
  • An uncompleted task returns to the original caller (at line 3) which will be completed in future (after completing task of like 8 and then running line 9).
  • Then the Result method at line 3 will be executed, which blocks the task until it gets completed and final result of GetDataAsync is ready.
  • When the awaited task of line 8 completes, the scheduler tries to run line 9 in the UI thread, but the UI thread is blocked! So line 9 cannot be executed and GetDataAsync cannot be completed.

A deadlock happens: UI thread is waiting for GetDataAsync to complete, and GetDataAsync is waiting for main thread to be free to execute rest of the code.

To avoid the deadlock, instead of getting the result of async method by Wait or Result, you should use await. But as I mentioned earlier, in above scenario, if you as a library developer run your task (here Task.Delay) by ConfigureAwait(false) it prevents deadlock, because it tells not to come back to the original context after this line and continue execution on thread pool thread. So at line 8, it tells to continue in thread pool thread, so while the UI thread is blocked, but line 9 executes and returns data to UI thread and unblock it.

// UI code 
1:  private void button1_Click(object sender, EventArgs e) 
2:  { 
3:      var result = GetDataAsync().Result; 
4:      MessageBox.Show(result); 
5:  } 

// A library code
6:  public async Task<string> GetDataAsync() 
7:  { 
8:      await Task.Delay(1000).ConfigureAwait(false); 
9:      return "Data"; 
10: } 

But again, keep in mind, as the UI developer, you should always use await for awaiting the Task and you should not use Result or Wait.




回答2:


we cannot using the async /await keyword

Why not? Doing synchronous work will block the UI thread, which will degrade the user experience. It's almost always better to use async/await.

it "Tells" the task

Technically, it tells the await. This is a common mistake. The method is ConfigureAwait, not ConfigureTask.

it can resume itself on any available thread instead of waiting to to the main thread.

Technically, it just avoids the context that is normally captured by await. For a UI thread, this context would normally resume on the UI thread. ConfigureAwait(false) causes await to avoid that context and just resume as though there was no context, i.e., on a thread pool thread.

Its preventing the deadlock by free the UI for not waiting for the long process to be completed and task is not waiting for the main UI thread to be available.

No. The deadlock occurs because the await is waiting for the UI thread to be free and the UI thread is blocked on the async method to complete. ConfigureAwait(false) avoids the deadlock by allowing the await to resume on a thread pool thread instead of the UI thread. The UI thread is still blocked on the async method. The UI thread is not freed up to do other work, and the user experience is still degraded.

How eventually UI thread update UI after long proccess task has completed.

In that code sample, the UI thread blocks on the asynchronous code, so it will block (become unresponsive) while that code is running. When the code completes, the UI thread will keep executing, just like any other code. At the end of that method, it returns to its message queue proc, and the application is responsive again.

While there can technically be more than one UI thread, having an application with more than one UI thread is rare. And each UI component would belong to a specific UI thread; they can't be shared.



来源:https://stackoverflow.com/questions/65603800/how-configureawaitfalse-prevent-ui-deadlocks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!