There is a method that accesses the server, and therefore uses approximately the following call:

public static async Task<Page> GetPageAsync(Uri url) { var webClient = new CookieClient(); await webClient.DownloadStringTaskAsync(url); return new Page(result, webClient.ResponseUri); } 

The method works, but it is usually used in others, and the above is as follows:

 public static async Task<string> GetName(Uri uri) { var name = string.Empty; var document = new HtmlDocument(); var page = await Page.GetPageAsync(uri); document.LoadHtml(page.Content); var nameNode = document.DocumentNode.SelectSingleNode("//head/title"); return WebUtility.HtmlDecode(nameNode.InnerHtml); } 

The essence of the problem is GetName(uri).Result hangs silently and does nothing, when trying to call a method like this in normal code. Taska is in the WaitingForActivation state.

Can I call async methods in synchronous code, or do I need to write similar synchronous code using separate methods?

  • And do not you call your Task.Result in a UI thread accidentally? - VladD
  • @VladD, in him. Vsmysle, somewhere else a couple of levels above the call from the UI. The problem is what? - Monk

1 answer 1

You have a deadlock.

See what the problem is.

You are blocked waiting for Task 'and in the UI-stream. This means that when you write GetName(uri).Result , your UI thread hangs pending the end of Task 'and GetName(uri) .

Why does not GetName(uri) terminate? We look at GetPageAsync(Uri url) . It starts to run in the UI thread too. The await webClient.DownloadStringTaskAsync(url) code await webClient.DownloadStringTaskAsync(url) goes to await webClient.DownloadStringTaskAsync(url) , and when the download is complete, should get control. This means that the thread where the signal about the end of the download was received, launches the rest of the method in the UI thread using Dispatcher.BeginInvoke or something like that.

But the UI thread is blocked! And waiting until the end of the .Result call. Therefore, it cannot work out the tail of the method.

So what to do in this case? There are two recommendations, and any one of them solves the problem (but it’s worth following both).

So, recommendation number 1: in the “library” code, like GetPageAsync or GetName , you don’t have to go back to the UI stream all the time. You can report this to the system with the following construct:

 result = await webClient.DownloadStringTaskAsync(url) .ConfigureAwait(continueOnCapturedContext: false); 

In this case, after the end of the wait in await code will not be transferred to the UI stream, but you don’t need it in this method.

All “library” procedures (which do not need a UI context) should be .ConfigureAwait(false) up with .ConfigureAwait(false) .

Recommendation number 2: Never block in a UI thread! If the asynchronous function takes a long time to complete, your UI will hang, which is bad. Try to pull async to the end. It is not at all difficult, by the way.

Additional reading on the topic: Stephen Cleary: Don't Block on Async Code .

  • On the first recommendation - does not help. Vsmysle, I added everywhere on the ConfigureAwait(false) stack ConfigureAwait(false) , but in my opinion it doesn’t even have time to reach them. - Monk
  • @Monk: So, somewhere along the way they forgot. Or you have two await methods. Check back! - VladD
  • one
    @Monk: You can check one at a time: first, call GetPageAsync(url).Result from a UI stream, if it works, then GetName(uri).Result , etc. - VladD
  • one
    @Monk: That the designer should make queries is wrong. The designer is obliged to work quickly, and the request is a slow thing. Each slow function should be Task idea, but the designer, of course, cannot be so. So refactoring is needed. - VladD
  • one
    @VladD, where third-party third parties require open constructors, so you have to think what to do. - Monk