Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are Async callback executed in WPF UI thread

Regarding this piece of code :

static async Task<string> testc()
{
    Console.WriteLine("helo async " + Thread.CurrentThread.ManagedThreadId); 
    await Task.Run(() => { 
        Thread.Sleep(1000);
        Console.WriteLine("task " + Thread.CurrentThread.ManagedThreadId); 
    });
    Console.WriteLine("callback "+Thread.CurrentThread.ManagedThreadId);
    return "bob";
}

static void Main(string[] args)
{
    Console.WriteLine("helo sync " + Thread.CurrentThread.ManagedThreadId);
    testc();
    Console.WriteLine("over" + Thread.CurrentThread.ManagedThreadId);
    Thread.Sleep(2000);
    Console.ReadLine();
}

I get the following output:

helo sync 10
helo async 10
over10
task 11
callback **11**

Which is OK : piece of code after await is executed in the same thread as task itself.

Now, if I do it in a WPF application :

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    Console.WriteLine("helo sync " + Thread.CurrentThread.ManagedThreadId);
    testc();
    Console.WriteLine("over" + Thread.CurrentThread.ManagedThreadId);
    Thread.Sleep(2000);
    Console.ReadLine();
}

It generates following output:

helo sync 8
helo async 8
over8
task 9
callback **8**

Where we can see code after await executed in the UI thread. Well, this is great since it enables manipulating observable collections etc... But I was wondering "Why?" "How could I do the same?" Is this related to some TaskScheduler behavior ? Is this hard-coded in the .NET Framework ?

Thx for any idea you may submit.

like image 336
Kek Avatar asked Apr 25 '13 06:04

Kek


1 Answers

The reason is that Task.Run will capture the SynchronizationContext if one is present as it is in a WPF app when starting the task from the UI thread. The Task will then use the SynchronizationContext to serialize the callback to the UI thread. However, if no context is awailable as in a Console app, the callback will happen on a different thread.

Stephen Toub has described this in a blog entry.

BTW, be careful when using never use Thread.Sleep in a Task. It may cause strange behaviour because a task may not be tied to one thread. Use Task.Delay instead.

like image 186
Jakob Christensen Avatar answered Sep 20 '22 00:09

Jakob Christensen