In the example below two await
calls are used. To gain performance, the sample gets converted Task.WaitAll()
instead (not really any faster, but th
When you're blocking the UI thread (and the current synchronization context) it will only cause a deadlock if one of the tasks that you're waiting on marshals a delegate to the current context and then waits on it (synchronously or asynchronously). Synchronously blocking on any async method isn't an instant deadlock in every single case.
Because async
methods will, by default, marshal the remainder of the method to the current synchronization context and after every single await
, and because the task will never finish until that happens, it means that synchronously waiting on methods that use async/await
will often deadlock; at least unless the described behavior is explicitly overridden (through, say ConfigureAwait(false)
).
Using WhenAll
means that you're not blocking the current synchronization context. Instead of blocking the thread you're just scheduling another continuation to be run when all of the other tasks finish, leaving the context free to handle any other requests that are ready right now (like, say, the continuation from the underlying async
method that WhenAll
is waiting on).
I describe the details of the deadlock situation on my blog. I also have an MSDN article on SynchronizationContext that you may find helpful.
In summary, Task.WaitAll
will deadlock in your scenario, but only if the tasks need to sync back to the UI thread in order to complete. You can conclude that CreateTableAsync<T>()
does not sync back to the UI thread.
In contrast, this code will deadlock:
public async Task SetupDatabaseAsync()
{
await CreateTableAsync<Session>();
await CreateTableAsync<Speaker>();
}
Task.WaitAll(SetupDatabaseAsync());
I recommend that you not block on asynchronous code; in the async
world, sync'ing back to the context is the default behavior (as I describe in my async intro), so it's easy to accidentally do it. Some changes to Sqlite.Net in the future may (accidentally) sync back to the original context, and then any code using Task.WaitAll
like your original example will suddenly deadlock.
It's best to use async
"all the way":
public Task SetupDatabaseAsync()
{
var t1 = CreateTableAsync<Session>();
var t2 = CreateTableAsync<Speaker>();
return Task.WhenAll(t1, t2);
}
"Async all the way" is one of the guidelines I recommend in my asynchronous best practices article.
Maybe this sample will demonstrate what might be happening. It's an iOS view loading. Try it with both the await call and without it (commented out below). Without any await in the function it will run synchronously and the UI will be blocked.
public async override void ViewDidLoad()
{
base.ViewDidLoad ();
var d1 = Task.Delay (10);
var d2 = Task.Delay (10000);
//await Task.Delay (10);
Task.WaitAll (d1, d2);
this.label.Text = "Tasks have ended - really!";
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear (animated);
this.label.Text = "Tasks have ended - or have they?";
}