28

I'm attempting to load and read a settings file on application launch, and about 90% of the time, the await GetFileAsync("filename.xml"); never returns, thus, hanging the application.

About a quarter of the time, if I step through the code, it'll actually return and read the file.

Here's a very simplified version of the code:

App.xaml.cs:

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    FileLoader.Load().Wait();

    // File-load dependent stuff
}

FileLoader.cs:

public async static Task Load()
{
    StorageFolder folder = ApplicationData.Current.LocalFolder;
    StorageFile file;
    bool fileExists = true;

    try
    {
        // The following line (often) never returns
        file = await folder.GetFileAsync("filename.xml");
    {
    catch
    {
        fileExists = false;
    }

    // Do stuff with loaded file
}

If I watch the Output window in Visual Studio, after awhile of waiting I get "The thread '<No Name>' (0x30c) has exited with code 0 (0x0)."

Does anyone have any idea of what's happening here?

jokeefe
  • 1,836
  • 4
  • 22
  • 27
  • 6
    You're blocking the UI thread as far as I can see by calling `Wait`. That's a really bad idea. Heck, that might even be what's causing the problem. – Jon Skeet Jul 03 '12 at 17:55
  • 1
    I think Jon's exactly right - any chance you can mark OnLaunched as async and then await the Load() call instead? If you can't, then another approach might be to have Load take an action to execute after the load has finished and then run the action at the end (or, of course, you can use ContinueWith on the task that Load() returns just like you would before async/await :) – James Manning Jul 03 '12 at 18:20
  • You were both right, Jon and James. Thank you very much for the input! I was originally using `Wait` because I didn't realize you could add `async` to an overridden method, and I didn't know that `OnLaunched` was running on the UI thread. Removing `Wait` and adding `async` to `OnLaunched` fixed it! I wish I could mark your comments as the answer. – jokeefe Jul 03 '12 at 18:35

1 Answers1

54

By default, when you await a Task that has not yet completed, the method resumes on a captured context (in this case, the UI context).

So, here's why your code is failing:

  • OnLaunched calls Load (within the UI context).
  • Load awaits. This causes the Load method to return an incomplete task and schedule its completion for later. This continuation is scheduled for the UI context.
  • OnLaunched blocks on the task returned from Load. This blocks the UI thread.
  • GetFileAsync eventually completes, and attempts to run the continuation for Load.
  • The continuation for Load waits for the UI thread to be available so it can execute in the UI context.
  • At this point, OnLaunched is waiting for Load to complete (blocking the UI thread by doing so), and Load is waiting for the UI thread to be free. Deadlock.

These best practices avoid this situation:

  1. In your "library" async methods, use ConfigureAwait(false) whenever possible. In your case, this would change await folder.GetFileAsync("filename.xml"); to await folder.GetFileAsync("filename.xml").ConfigureAwait(false);.
  2. Don't block on Tasks; it's async all the way down. In other words, replace Wait with await.

For more information:

Update, 2012-07-13: Incorporated this answer into a blog post.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Excellent answer! Thank you very much for the detailed explanation of what's happening and the extensive list of reference articles. This makes everything much clearer. – jokeefe Jul 05 '12 at 14:48
  • This was perfect. This saved me so much grief it was unbelievable. – ohmusama Aug 16 '13 at 23:44
  • 2
    I wish MS to be dead. They are definitely seek people due to providing such crazy things. Why the hell they just didn't left old-fashion threads and sync primitives?! Morons. –  May 23 '16 at 15:10
  • @AlekDepler: [Asynchronous programming is the opposite of multithreading](http://blog.stephencleary.com/2013/11/there-is-no-thread.html). While you can "fake" *some* async programming using threads, you cannot *always* use threads to solve asynchronous programming requirements. – Stephen Cleary May 23 '16 at 15:22
  • 1
    I can and I do. "Fake" is the wheels that MS invent every year. For now, the only requirement I need in WPF is simple threads. Tired... –  May 23 '16 at 15:44
  • absolutely agree, msft should burn in hell with its bunch of async paradigms, new one each year. It looks like a bad movie - money are spend, people hate it, and producer spends millions again for reviews, ads, movie about movie to make us believe his movie was great. So many articles, lessons, videos about Tasks. Ok, but why should I spend hours to make this piece of sh*t to read local files?! F**k!! Local files, Carl! – Tertium Jan 26 '17 at 16:37