Synchronously Waiting for an Async Operation, and Why Does Wait() Freeze the Program Here

Synchronously waiting for an async operation, and why does Wait() freeze the program here

The await inside your asynchronous method is trying to come back to the UI thread.

Since the UI thread is busy waiting for the entire task to complete, you have a deadlock.

Moving the async call to Task.Run() solves the issue.

Because the async call is now running on a thread pool thread, it doesn't try to come back to the UI thread, and everything therefore works.

Alternatively, you could call StartAsTask().ConfigureAwait(false) before awaiting the inner operation to make it come back to the thread pool rather than the UI thread, avoiding the deadlock entirely.

Trying to call Async method synchronously. It waits on Task.Result forever

This is a classic deadlock. The UI is waiting on the async method to finish, but the async method trys to update the UI thread and BOOM, deadlock.

Curiously though, if I copy this pattern more or less verbatim into a
Console app, it works.

That's because your WinForm application has a custom SynchronizationContext. It is caputred implicitly and its job is to marshal work back onto the UI thread once returning from your await.

Should you really expose synchronous wrappers around asynchronous operations?, the answer is no.

There is a way out of it, but i dont really like it. If you absolutely have to (you don't) call your code synchronously (again, you really shouldn't), use ConfigureAwait(false) inside the async method. This instructs the awaitable not to capture the current synccontext, so it wont marshal work back onto the UI thread:

public async Task<string> MyMethodAsync() 
{
using (var cl = new HttpClient())
{
return await cl.GetStringAsync("http://www.google.co.uk/")
.ConfigureAwait(false);
}
}

Note that if you do this and then try to call any UI element afterwards, you'll end up with an InvalidOperationException since you won't be on the UI thread.

Initializing the UI via a constructor is a common pattern. Stephan Cleary has a very nice series on async which you can find here.

What am I doing wrong? Is this simply a bad pattern, and if so, what
pattern should I be using instead?

Yes, absolutely. If you want to expose both asynchronous and synchronous APIs, use the proper api's which wouldn't get you into this situation (a deadlock) in the firstcase. For example, if you want to expose a synchronous DownloadString, use WebClient instead.

Calling async function in synchronous function doesn't wait for it to finish

Following Panagiotis Kanavos's advice:

Tasks aren't threads. There's no good reason to call Task.Start in
application code. How is checkInstalled called? That method should be
asynchronous itself, going all the way to the top-level event handler
that started these calls. The event handler should be async void but
all the other methods should be async Task. Calling .Wait() will
freeze the UI

After making every necessary function async and using await when needed I managed to get everything working the way I wanted

Windows Store Application - Synchronously waiting for an async operation

This is not a solution for doing anything synchronously, it's simply how to avoid calling back to the UI thread when you don't need to. The problem with your actual code is that you've defined an extension for IAsyncOperation<T>, not IAsyncAction.

Asynchronously wait for Task T to complete with timeout

How about this:

int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}

And here's a great blog post "Crafting a Task.TimeoutAfter Method" (from MS Parallel Library team) with more info on this sort of thing.

Addition: at the request of a comment on my answer, here is an expanded solution that includes cancellation handling. Note that passing cancellation to the task and the timer means that there are multiple ways cancellation can be experienced in your code, and you should be sure to test for and be confident you properly handle all of them. Don't leave to chance various combinations and hope your computer does the right thing at runtime.

int timeout = 1000;
var task = SomeOperationAsync(cancellationToken);
if (await Task.WhenAny(task, Task.Delay(timeout, cancellationToken)) == task)
{
// Task completed within timeout.
// Consider that the task may have faulted or been canceled.
// We re-await the task so that any exceptions/cancellation is rethrown.
await task;

}
else
{
// timeout/cancellation logic
}


Related Topics



Leave a reply



Submit