WinRT metro app still blocks the UI thread when called via an async method

Go To StackoverFlow.com

3

I have a simple metro app contains a button, a label and a drop down list. The drop down list contains a list of files that I can read from. When I click on the button, the selected file’s content gets read into the label. Files are in the Documents folder (i.e WinRT’s KnownFolders.DocumentsLibrary). Each file represents a StorageFile in WinRT API.

File reading method is an asynchronous method (uses async/await). In order to prove the asynchronous behaviour, I made the file reading method as a long running process. Therefore, while executing this long running method, I should be able to freely click on the drop down list and select a different file. This is because the UI thread should not get blocked while reading the file. However this is not currently happening. It still seems to be blocking UI thread, and drop down list get frozen while the long running method occurs. I must be doing something odd here. Can you please tell me why the UI is non responsive? My code sample is below.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace FileAccess
{
    public sealed partial class MainPage : Page
    {
       private readonly StorageFolder storageFolder;       

       public MainPage()
       {
        this.InitializeComponent();
        storageFolder = KnownFolders.DocumentsLibrary;
        AddFiles();
       }

       private async void AddFiles() //Add files to the drop down list
       {
        var filesList = await storageFolder.GetFilesAsync();
        IEnumerable<FileItem> fileItems
            = filesList.Select(x => new FileItem(x.Name, x.Name));

        cboSelectFile.ItemsSource = fileItems;
        cboSelectFile.SelectedIndex = 0;
       }


      private async void BtnReadFile_Click(object sender, RoutedEventArgs e)
      {
        IStorageFile storageFile = await storageFolder.GetFileAsync((string)cboSelectFile.SelectedValue);           

        if (storageFile != null)
        {
            LblReadFile.Text = await ReadFileAsync(storageFile); //long running method**************
        }
      }


      private async Task<string> ReadFileAsync(IStorageFile storageFile) //long running method**************
      {
        var fileContent = await FileIO.ReadTextAsync(storageFile);

        for (Int64 i = 0; i < 10000000000; i++)
        {
        }

        return fileContent; 
      }                
  }

}

2012-04-03 21:36
by Spock
Well, holy cow, how many files are in that directory - Hans Passant 2012-04-03 22:36
@HansPassant only few, but I'm curious the reason for your question - Spock 2012-04-04 00:28


7

If you execute code like this on a UI thread:

var whatever = await DoSomethingAsync();
// some more code

Then // some more code will also execute on the UI thread. Which is exactly your problem. After the file is read, you execute the long loop on the UI thread, which is why the UI freezes.

If you want to simulate some long-running operation, you can do it several ways:

  1. Execute the loop on a background thread using Task.Run(), and asynchronously wait for it to finish:

    private async Task<string> ReadFileAsync(IStorageFile storageFile)
    {
        var fileContent = await FileIO.ReadTextAsync(storageFile);
    
        await Task.Run(() => { for (Int64 i = 0; i < 10000000000; i++) {} });
    
        return fileContent; 
     }
    
  2. Don't waste CPU time and use Task.Delay() to delay executing of the code:

    private async Task<string> ReadFileAsync(IStorageFile storageFile)
    {
        var fileContent = await FileIO.ReadTextAsync(storageFile)
                                      .ConfigureAwait(false);
    
        await Task.Delay(TimeSpan.FromSeconds(10));
    
        return fileContent; 
     }
    
2012-04-03 22:01
by svick
That seemed like the obvious answer, but I didn't know how to explain the fact that the containing method is also awaited - Ritch Melton 2012-04-03 22:03
@RitchMelton, that doesn't change anything. Why do you think it would - svick 2012-04-03 22:08
Ignorance. I assumed that an await method that was marked async has its entire contents as async since it returns a task - Ritch Melton 2012-04-03 22:09
Well, its content is async, in that there is no thread synchronously waiting for FileIO.ReadTextAsync(). But if it starts on the UI thread, it also continues there by default - svick 2012-04-03 22:12
Ok, I that makes sense. I would have suggested (1), but I was guessing at that point. Thanks for the info - Ritch Melton 2012-04-03 22:13