为什么等待有时会创建新线程,但有时不会?

yor*_*ork 0 c# multithreading asynchronous async-await

    class Program
{
    static void Main(string[] args)
    {
        var rst = DownloadPage("http://www.baidu.com");
        //var rst2=GetString();

        Console.ReadKey();
    }

    private static async Task<string> DownloadPage(string url)
    {
        using (var client = new HttpClient())
        {
            PringMsgWithThreadId("Before await");
            var response = await client.GetAsync(url).ConfigureAwait(continueOnCapturedContext:false);
            var content= await response.Content.ReadAsStringAsync();

            PringMsgWithThreadId(content.Substring(0, 10));
            PringMsgWithThreadId("After await");
            return content;
        }
    }

    private static async Task<string> GetString()
    {
        PringMsgWithThreadId("Before await");
        var result = await GetStringAsync();

        PringMsgWithThreadId(result);
        PringMsgWithThreadId("After await");
        return result;
    }

    private static Task<string> GetStringAsync()
    {
        var task = new Task<string>(() =>
          {
              Thread.Sleep(1000 * 2);
              return "string after sleep two seconds";
          });
        task.RunSynchronously();
        return task;
    }

    private static void PringMsgWithThreadId(string tag)
    {
        Console.WriteLine($"{tag}(ThreadId:{Thread.CurrentThread.ManagedThreadId})");
    }
}
Run Code Online (Sandbox Code Playgroud)

运行时输出DownloadPage()方法输出:

在此输入图像描述

运行GetString()方法时的输出

在此输入图像描述

我的问题:

1.当调用DownloadPage()时,为什么代码在主线程(ThreadId:10)以外的线程(ThreadID:15)中执行等待.

2.当调用GetString()时,为什么在同一个线程中执行await之后的代码(两个threadId都是10).

Ste*_*ary 8

await 从不创建新线程.

正如我的async介绍中所解释的,await将首先检查其论点(任务).如果已经完成,则继续同步执行.否则,它"暂停"该方法并使用其参数注册回调(即,在任务上放置一个延续).

稍后,当任务完成时,将继续运行.由于您在没有SynchronizationContext/ 的控制台应用程序中TaskScheduler,该延续将在线程池线程上运行.

所以,你的第一个问题的答案是主线程忙(阻塞Console.ReadKey),并且控制台应用程序中的主线程无论如何都不是线程池线程.第二个问题的答案是因为任务在同步GetStringAsync运行并且在它返回时已经完成,这导致in 继续(同步).awaitGetString

另外,您永远不应该使用任务构造函数.如果要返回已完成的任务,请使用Task.FromResult.如果要在后台线程上执行某些工作,请使用Task.Run.