Spock
Spock

Reputation: 6992

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

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; 
      }                
  }

}

Upvotes: 3

Views: 5278

Answers (1)

svick
svick

Reputation: 244968

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; 
     }
    

Upvotes: 7

Related Questions