0

I'm investigating async/await.

And I had a question at C# winform: what's wrong in this async example if access result before await The above question has been resolved. This is a deadlock issue.

But when I searched some question in stackoverflow. I found this: async/await different thread ID

It should be also a deadlock issue, isn't it?

I rewrite a similar code to test

    private async void button1_Click(object sender, EventArgs e)
    {
        TB_Log(tbMsg, "click start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        Task task = func1();
        TB_Log(tbMsg, "click wait, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await task;
        TB_Log(tbMsg, "click end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
    }

    private async Task func1()
    {
        TB_Log(tbMsg, "func1 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await Task.Run(() =>
        {
            TB_Log(tbMsg, "func1 task.run, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Task<int> task = func2();
            if (task.Result == 5) { ; }
        });
        TB_Log(tbMsg, "func1 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
    }

    private async Task<int> func2()
    {
        TB_Log(tbMsg, "func2 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await Task.Run(() =>
        {
            TB_Log(tbMsg, "func2 task.run,  thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        });
        TB_Log(tbMsg, "func2 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());

        return 1;
    }

And the code runs well, won't block. the code result shows:

click start, thread id:1
func1 start, thread id:1
click wait, thread id:1
func1 task.run, thread id:3
func2 start, thread id:3
func2 task.run,  thread id:4
func2 end, thread id:4
func1 end, thread id:1
click end, thread id:1

The task.Result deadlock will not occur.

My question is:

  1. Why task.Result won't cause a deadlock?
  2. Why func1 end, thread id is 1, not 4 or 3?

About the 2. question, I opened a Console project to replace the Winform project.

The result is a little different.

    static async Task Main(string[] args)
    {
        Console.WriteLine("Main Start, thread id:" + Thread.CurrentThread.ManagedThreadId);
        Task task = func1();
        await task;
        Console.WriteLine("Main End, thread id:" + Thread.CurrentThread.ManagedThreadId);
        Console.ReadKey();
    }

    static private async Task func1()
    {
        Console.WriteLine("func1 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await Task.Run(() =>
        {
            Console.WriteLine("func1 task.run, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
            Task<int> task = func2();
            if (task.Result == 5) {; }
        });
        Console.WriteLine("func1 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
    }

    static private async Task<int> func2()
    {
        Console.WriteLine("func2 start, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        await Task.Run(() =>
        {
            Console.WriteLine("func2 task.run,  thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());
        });
        Console.WriteLine("func2 end, thread id:" + Thread.CurrentThread.ManagedThreadId.ToString());

        return 1;
    }
Main Start, thread id:1
func1 start, thread id:1
func1 task.run, thread id:4
func2 start, thread id:4
func2 task.run,  thread id:5
func2 end, thread id:5
func1 end, thread id:4
Main End, thread id:4

The Main End id is not yet be 1.

Thanks you a lot. Because this is really confusing me.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
honoyang
  • 3
  • 1

1 Answers1

1

Remember, what was the key issue for the deadlock to occur in your other question?

It was accessing Task.Result in the UI thread (the click handler) and thus blocking the UI thread, with the continuation part of the task (the return 1; also requiring to run on the UI thread, but which is blocked, hence no cake...

Do you access Task.Result in the UI thread here in this code example? No. So, no deadlock, eat your cake... (All direct accesses of some Task.Result happen within some tasks/functions that are executed through Task.Run and thus running in some background worker thread from the thread pool, thus not involving the UI thread.)

The difference you observed in the console application has all to do with synchronization contexts. In short, synchronization contexts determine on which thread a continuation of a task (continuations are the pieces of code in an async function following an awaited task up until the next awaited task or the end of the function) will run. And synchronization contexts are determined differently for console applications compared to GUI apps. You can read about all that dirty laundry here: https://devblogs.microsoft.com/pfxteam/await-synchronizationcontext-and-console-apps/