I'm just experimenting with WinRT and one demo app i'm creating is a basic "notepad" style app which loads/saves to local storage. Whilst I'm familiar with the proper async
approach for building WinRT apps, my demo app is using a synchronous Load
to keep things simple.
The problem is that when a call is made to Load
, it works 2 out of 3 times and the rest of the time the app hangs on the call var result = await FileIO.ReadTextAsync(storageFile);
public class ContentStorage : IContentStorage
{
private const string FileName = "contents.txt";
public string Load()
{
return LoadAsync().Result;
}
public void Save(string content)
{
SaveAsync(content);
}
private static async Task<string> LoadAsync()
{
var storageFile = await LocalFolder.GetFileAsync(FileName);
var result = await FileIO.ReadTextAsync(storageFile);
return result;
}
private static async void SaveAsync(string content)
{
var storageFile = await LocalFolder.CreateFileAsync(FileName, CreationCollisionOption.ReplaceExisting);
FileIO.WriteTextAsync(storageFile, content);
}
private static StorageFolder LocalFolder
{
get { return ApplicationData.Current.LocalFolder; }
}
}
Am I doing something extraordinarily stupid here?
FWIW, I experimented with changing Load
to just explicitly block on each step and this improves the hanging to 1 in 20, but I still don't understand why it's hanging at all...
public string Load()
{
var storageFile = LocalFolder.GetFileAsync(FileName).AsTask().Result;
var result = FileIO.ReadTextAsync(storageFile).AsTask().Result;
return result;
}
Not really. Mixing synchronous with asynchronous code is extremely complex. It's far simpler to just use
async
everywhere.When an
async
method continues execution after waiting for a task, it will return to its original context by default. (I cover this in more detail in myasync
/await
blog post). Some contexts (such as UI contexts) only permit a single thread; if that thread is blocked (e.g., onTask.Result
), then theasync
method cannot enter that context to complete its execution. This causes a deadlock.For more information:
async
/await
FAQ has a lot of detail on the context capture and resume.This deadlock is famous enough that it's actually been demo'd by Microsoft:
Try to use
ConfigureAwait(false
) with the await operation, may beReadTextAsync
is not thread safe, so it will hang the UI thread when the await finished and back to the UI thread.