【问题标题】:Why await sometimes create new thread but sometimes not?为什么 await 有时会创建新线程但有时不会?
【发布时间】:2017-04-09 10:16:26
【问题描述】:
    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})");
    }
}

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

运行GetString()方法时的输出

我的问题:

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

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

【问题讨论】:

  • 用什么语言?

标签: c# multithreading asynchronous async-await


【解决方案1】:

await从不创建新线程。

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

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

所以,第一个问题的答案是主线程很忙(在Console.ReadKey 中被阻塞),而且控制台应用程序中的主线程也不是线程池线程。第二个问题的答案是因为GetStringAsync 中的任务是同步运行 并且在它返回时已经完成,这会导致GetString 中的await 继续(同步) .

附带说明,您永远不应该使用任务构造函数。如果要返回已完成的任务,请使用Task.FromResult。如果你想在后台线程上执行一些工作,请使用Task.Run

【讨论】:

  • 谢谢!你的回答和文章让我对 async/await 有深刻的理解。