1

I am quite new to async await. I think I understood the example of the console application. When transferring the identical code to the WPF, there is a deadlock and I do not know exactly why.

// All as expected
// Output:
// LongOperationAsync Start
// Before task.Wait();
// LongOperationAsync End
// After task.Wait();
// Result: 4711

class Program {
    public static void Main() {
        Task<int> task = LongOperationAsync();
        //Console.ReadKey();
        Console.WriteLine("Before task.Wait();");
        task.Wait();
        Console.WriteLine("After task.Wait();");
        var result = task.Result;
        Console.WriteLine("Result: {0}", result);
        Console.ReadKey();
    }

    static async Task<int> LongOperationAsync() {
        Console.WriteLine("LongOperationAsync Start");
        await Task.Delay(1000);
        Console.WriteLine("LongOperationAsync End");
        return 4711;
    }
}

and here is the blocking WPF code:

// WPF handler Output:
// LongOperationAsync Start
// Before task.Wait(); => Blocking

private void button2_Click(object sender, EventArgs e) {
    Task<int> task = LongOperationAsync();
    Debug.WriteLine("Before task.Wait();");
    task.Wait();
    Debug.WriteLine("After task.Wait();");
    var result = task.Result;
    Debug.WriteLine("Result: {0}", result);
}

private async Task<int> LongOperationAsync() {
    Debug.WriteLine("LongOperationAsync Start");
    await Task.Delay(1000);
    Debug.WriteLine("LongOperationAsync End");
    return 4711;
}
karel
  • 5,489
  • 46
  • 45
  • 50
A5000
  • 11
  • 1
  • 1
    The code blocks in both cases. Both examples are bad – Panagiotis Kanavos Feb 27 '19 at 09:40
  • In "async world" you should **never** do any `task.Wait()`'s or `task.Result`'s. – AgentFire Feb 27 '19 at 09:41
  • There is too much information to this to answer. this is a broad topic, all i can suggest is to keep doing more research. read about synchronization contexts, read about deadlocks, look up blogs by stephen cleary, and make use of google, you'll get there. – TheGeneral Feb 27 '19 at 09:42
  • 1
    I think you can read this post https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – Ha Pham Feb 27 '19 at 09:48
  • Although advising to read more is fair advice, I think we can do better. Panagiotis posted almost exactly the code I was about to and I think showing the "right" code is useful. – Andy Feb 27 '19 at 09:56
  • @A5000: `I am quite new to async await.` I recommend starting with my [async intro](https://blog.stephencleary.com/2012/02/async-and-await.html) and then [async best practices](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx). – Stephen Cleary Feb 27 '19 at 13:02

2 Answers2

5

The code blocks in both cases. Both examples are bad. Neither executes asynchronously.

The correct Console code would be :

public static async Task Main()
{
    Console.WriteLine("Before LongOperationAsync");

    int result = await LongOperationAsync();

    Console.WriteLine("After LongOperationAsync");
    Console.WriteLine("Result: {0}", result);
    Console.ReadKey();
}

The equivalent WPF code would be :

private async void button2_Click(object sender, EventArgs e)
{
    Debug.WriteLine("Before LongOperationAsync");

    int result = await LongOperationAsync();

    Debug.WriteLine("After LongOperationAsync");
    Debug.WriteLine("Result: {0}", result);
}

async is syntactic sugar that allows the use of the await keyword. await awaits asynchronously for an already asynchronous operation like Task.Delay() to complete, without blocking the original thread. When that asynchronous operation completes, await resumes execution in the original synchronization context. In the WPF application's case, that's the UI thread. That's what allows updating the UI after an await.

async void is only meant for event handlers, like button2_click. In all other cases async Task should be used. async void methods can't be awaited and the application can't know whether they completed or not.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
-1

In your example you can modify your button-click to async. Your Task is blocking because you never start him, you can never pass the "wait()" call without starting the task.

Below the modified button click method.

private async void button2_Click(object sender, EventArgs e)
{
    var result = await LongOperationAsync();
    Debug.WriteLine("Result: {0}", result);
}
CGuy
  • 38
  • 4
  • The task is already started. The actual asynchronous task is the one generated by `Task.Delay()`. `await` doesn't start anything, it awaits already executing tasks – Panagiotis Kanavos Feb 27 '19 at 09:50