为什么在WPF UI线程中执行异步回调

Kek*_*Kek 7 c# wpf asynchronous

关于这段代码:

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();
}
Run Code Online (Sandbox Code Playgroud)

我得到以下输出:

helo sync 10
helo async 10
over10
task 11
callback **11**
Run Code Online (Sandbox Code Playgroud)

哪个是好的:await之后的代码片段与任务本身在同一个线程中执行.

现在,如果我在WPF应用程序中执行此操作:

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();
}
Run Code Online (Sandbox Code Playgroud)

它生成以下输出:

helo sync 8
helo async 8
over8
task 9
callback **8**
Run Code Online (Sandbox Code Playgroud)

我们可以在UI线程中执行await后看到代码.嗯,这很棒,因为它可以操纵可观察的集合等...但我想知道"为什么?" "我怎么能这样做?" 这与某些TaskScheduler行为有关吗?这是在.NET Framework中进行硬编码的吗?

感谢您提交的任何想法.

Jak*_*sen 7

原因是SynchronizationContext当从UI线程启动任务时,Task.Run将捕获if存在于WPF应用程序中的情况.然后,Task将使用SynchronizationContext序列化回调到UI线程.但是,如果在控制台应用程序中没有上下文可用,则回调将在不同的线程上发生.

Stephen Toub在博客文章中对此进行了描述.

顺便说一句,使用时请小心,不要在任务中使用Thread.Sleep.它可能会导致奇怪的行为,因为任务可能不会绑定到一个线程.请改用Task.Delay.