为什么这两个关于 Task 的 C# 代码块的行为不同?

问题描述 投票:0回答:1

我正在研究C# Task、async/await,遇到了不太明白的情况。我制作了两个代码片段,第一个是基于控制台应用程序,第二个是基于wpf。这是两个代码片段:

  1. 控制台应用程序代码:
async static Task Main()
{
    Helper.PrintThreadId("Before");
    await FooAsync();
    Helper.PrintThreadId("After");
}

async static Task FooAsync()
{
    Helper.PrintThreadId("Before");
    await Task.Delay(1000);
    Helper.PrintThreadId("After");
}

class Helper
{
    private static int index = 1;
    public static void PrintThreadId(string message = null, [CallerMemberName] string name = null)
    {
        var title = $"{index}: {name}";
        if (!string.IsNullOrEmpty(message))
            title += $" @ {message}";
        Console.WriteLine("Thread ID: " + Environment.CurrentManagedThreadId + ", title: " + title);
        Interlocked.Increment(ref index);
    }
}
  1. wpf 代码(助手与控制台应用程序中的完全相同):
async Task<int> HeavyJob()
{
    Helper.PrintThreadId("Before");
    await Task.Delay(3000);
    Helper.PrintThreadId("After");
    return 10;
}

private async void Button_Click(object sender, RoutedEventArgs e)
{
    Helper.PrintThreadId("Before");
    var res = await HeavyJob().ConfigureAwait(false);
    Helper.PrintThreadId("After");
}

class Helper
{
    private static int index = 1;
    public static void PrintThreadId(string message = null, [CallerMemberName] string name = null)
    {
        var title = $"{index}: {name}";
        if (!string.IsNullOrEmpty(message))
            title += $" @ {message}";
        Debug.WriteLine("Thread ID: " + Environment.CurrentManagedThreadId + ", title: " + title);
        Interlocked.Increment(ref index);
    }
}

那么,对于控制台应用程序,结果是:

![enter image description here

对于wpf,结果是:

![enter image description here

我的问题是两个结果的第三行。为什么这两者的行为不同?据我了解,第三行和第四行的线程ID应该是相同的。

我希望有人能解释一下这个问题。谢谢。

c# task
1个回答
0
投票

HeavyJob()
中的 WPF 代码中,您有以下内容:

await Task.Delay(3000);

没有

.ConfigureAwait(false)
,所以对于WPF(或WinForms)应用程序来说,它将在启动它的同一线程上恢复 - 在您的示例中,这将是线程1。它可以做到这一点是因为
SynchronizationContext
能够使用 Windows 消息循环在 UI 线程上恢复。

类似地,在

FooAsync()
中的控制台代码中,您也有一个没有
.ConfigureAwait(false)
的等待。但是,对于控制台应用程序,默认情况下没有
SynchronizationContext
可用于在调用线程上恢复。因此,在这种情况下,等待将在新线程上恢复。

© www.soinside.com 2019 - 2024. All rights reserved.