First what is deadlock?
A deadlock occurs when two entities are waiting on each other, preventing either from moving forward. In multithreading, a deadlock happens when two or more threads are each waiting for resources locked by the other.
For example, imagine two lock objects: _lockA and _lockB.
- Thread 1 acquires
_lockAand then waits for_lockB. - Thread 2 acquires
_lockBand then waits for_lockA.
Since both threads are holding one lock while waiting for the other to be released, neither can proceed. This circular waiting results in a deadlock.
So what is the deadlock in the context of asynchronous programming. It is the same definition, it is when two things are waiting on each others but in what are those two things we are talking about in term of asynchronous. Let have a look at this example:
var result = GetDataAsync().Result;
public async Task<string> GetDataAsync()
{
await Task.Delay(1000); // async wait
return "Done";
}
- UI thread call .Result() so blocked waiting for GetDataAsync() to complete and it is a synchronous wait like .Wait()
Task.Delay(1000)completes after 1 second and tries to run the continuation (return "Done";) back on the UI thread.- But the UI thread is block so it can’t return hence GetDataAsync() can’t complete
- So Deadlock, UI thread block waiting GetDataAsync() to complete but GetDataAsync cannot complete because UI thead is blocked.
To avoid it, never use .Result() or .Wait() because it is a main thread blocking so instead use await to not blocking the main thread or use .ConfigureAwait(false) to not return back (resume) onto the main thread. Though note that if you use .ConfigureAwait(false) we won’t be on UI thread (the calling thread) so if we need to update UI control we might have to marshal the call back on UI thread using controls .invoke:
e.g
myButton.Invoke(() =>
{
myButton.Text = "Clicked!";
});
