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

C#.NetTask Parallel-LibraryWindows Store-AppsAsync Await

C# Problem Overview

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",
    await FileIO.AppendTextAsync(file, text,

Now the corresponding synchronous method...

Version 1:

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

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

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

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

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?

C# Solutions

Solution 1 - C#

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.

Solution 2 - C#

Calling async code from synchronous code can be quite tricky.

I explain the full reasons for this deadlock on my blog. In short, there's a "context" that is saved by default at the beginning of each await and used to resume the method.

So if this is called in an UI context, when the await completes, the async method tries to re-enter that context to continue executing. Unfortunately, code using Wait (or Result) will block a thread in that context, so the async method cannot complete.

The guidelines to avoid this are:

  1. Use ConfigureAwait(continueOnCapturedContext: false) as much as possible. This enables your async methods to continue executing without having to re-enter the context.
  2. Use async all the way. Use await instead of Result or Wait.

If your method is naturally asynchronous, then you (probably) shouldn't expose a synchronous wrapper.

Solution 3 - C#

Here is what I did

private void myEvent_Handler(object sender, SomeEvent e)
  // I dont know how many times this event will fire
  Task t = new Task(() =>
    if (something == true) 

working great and not blocking UI thread

Solution 4 - C#

With small custom synchronization context, sync function can wait for completion of async function, without creating deadlock. Here is small example for WinForms app.

Imports System.Threading
Imports System.Runtime.CompilerServices

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    End Sub

    ' waiting inside Sync method for finishing async method
    Public Sub SyncMethod()
        Dim sc As New SC
    End Sub

    Public Async Function AsyncMethod() As Task(Of Boolean)
        Await Task.Delay(1000)
        Return True
    End Function

End Class

Public Class SC
    Inherits SynchronizationContext

    Dim OldContext As SynchronizationContext
    Dim ContextThread As Thread

    Sub New()
        OldContext = SynchronizationContext.Current
        ContextThread = Thread.CurrentThread
    End Sub

    Dim DataAcquired As New Object
    Dim WorkWaitingCount As Long = 0
    Dim ExtProc As SendOrPostCallback
    Dim ExtProcArg As Object

    Public Overrides Sub Post(d As SendOrPostCallback, state As Object)
        ExtProc = d
        ExtProcArg = state
    End Sub

    Dim ThreadSleep As Long = 0

    Private Sub AwakeThread()
        If Interlocked.Read(ThreadSleep) > 0 Then ContextThread.Resume()
    End Sub

    Public Sub WaitForTask(Tsk As Task)
        Dim aw = Tsk.GetAwaiter

        If aw.IsCompleted Then Exit Sub

        While Interlocked.Read(WorkWaitingCount) > 0 Or aw.IsCompleted = False
            If Interlocked.Read(WorkWaitingCount) = 0 Then
                Dim Proc = ExtProc
                Dim ProcArg = ExtProcArg
            End If
        End While

    End Sub

     Public Sub Release()
     End Sub

End Class

Solution 5 - C#

For me actually the best working solution:


Works also on UI-Content without blocking and dispatcher problems, and also from CTOR's.


All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSebastian NegraszusView Question on Stackoverflow
Solution 1 - C#SLaksView Answer on Stackoverflow
Solution 2 - C#Stephen ClearyView Answer on Stackoverflow
Solution 3 - C#pixelView Answer on Stackoverflow
Solution 4 - C#codefoxView Answer on Stackoverflow
Solution 5 - C#ALEX-74-DEView Answer on Stackoverflow