Monday, 11 December 2017

c# - Synchronously waiting for an async operation, and why does Wait() freeze the program here

itemprop="text">

Preface:
I'm looking for an explanation, not just a solution. I already know the
solution.



Despite having spent several days
studying MSDN articles about the Task-based Asynchronous Pattern (TAP), async and await,
I'm still a bit confused about some of the finer
details.



I'm writing a logger for Windows Store
Apps, and I want to support both asynchronous and synchronous logging. The asynchronous
methods follow the TAP, the synchronous ones should hide all this, and look and work
like ordinary methods.



This is the core method
of asynchronous
logging:




private async
Task WriteToLogAsync(string text)
{
StorageFolder folder =
ApplicationData.Current.LocalFolder;
StorageFile file = await
folder.CreateFileAsync("log.log",

CreationCollisionOption.OpenIfExists);
await FileIO.AppendTextAsync(file,
text,

Windows.Storage.Streams.UnicodeEncoding.Utf8);
}



Now
the corresponding synchronous
method...



Version
1
:



private void
WriteToLog(string text)
{
Task task =
WriteToLogAsync(text);

task.Wait();
}



This
looks correct, but it does not work. The whole program freezes
forever.



Version
2
:



Hmm.. Maybe the task was not
started?



private void
WriteToLog(string text)
{

Task task =
WriteToLogAsync(text);
task.Start();

task.Wait();
}


This
throws InvalidOperationException: Start may not be called on a promise-style
task.



Version
3:




Hmm..
Task.RunSynchronously sounds
promising.



private void
WriteToLog(string text)
{
Task task =
WriteToLogAsync(text);

task.RunSynchronously();
}


This
throws InvalidOperationException: RunSynchronously may not be called on a
task not bound to a delegate, such as the task returned from an asynchronous
method.




Version
4 (the
solution):



private
void WriteToLog(string text)
{
var task = Task.Run(async () => {
await WriteToLogAsync(text); });

task.Wait();
}



This
works. So, 2 and 3 are the wrong tools. But 1? What's wrong with 1 and what's the
difference to 4? What makes 1 cause a freeze? Is there some problem with the task
object? Is there a non-obvious deadlock?


class="post-text" itemprop="text">
class="normal">Answer



The
await inside your asynchronous method is trying to come back to
the UI thread.



Since the UI thread is busy
waiting for the entire task to complete, you have a
deadlock.



Moving the async call to
Task.Run() solves the issue.
Because the async call
is now running on a thread pool thread, it doesn't try to come back to the UI thread,
and everything therefore works.



Alternatively,
you could call StartAsTask().ConfigureAwait(false) before
awaiting the inner operation to make it come back to the thread pool rather than the UI
thread, avoiding the deadlock entirely.


No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...