异步程序的奇怪行为不是 运行 异步

Strange behavior with asynchronous program not running asynchronously

我在下面的程序中打印了哪个线程执行每个调用的方法,结果很奇怪。我希望所有异步调用都由线程池线程执行,因为@Jon Skeet 提到异步调用使用 ThreadPool 线程并且线程池线程是后台线程。

线程id表明执行Main方法的同一个线程执行所有被调用的方法,除了DisplayResult不是异步方法。

为什么不是异步方法的 DisplayResult 的每次调用都由线程池线程执行,而实际的异步方法在被调用时是由非后台线程执行的,同一个非后台线程执行了Main方法?以及这个程序应该有多少个后台线程和非后台线程?

public static async Task Main()
        {
            Console.WriteLine($"Main method: The thread executing this task is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");

            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            Task task1 = ProcessReadWriteAsync(@"/tmp/temp1Write.txt");

            Task task2 = ProcessReadWriteAsync(@"/tmp/temp2Write.txt");

            await task1;
            await task2;
        }

        public static async Task ProcessReadWriteAsync(string filePath)
        {
            Console.WriteLine($"ProcessReadWriteAsync: The thread executing this task is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");
            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            try
            {
            await ReadWriteAsync(filePath);

            } catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            } finally
            {
                Console.WriteLine();
            }
        }

        public static async Task ReadWriteAsync(string path, string text)
        {
            Console.WriteLine($"ReadWriteAsync 2 parameters: The thread executing this task is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");

            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            FileStream stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, useAsync: true);

                byte[] buffer = new byte[0x1000];

                int noOfCharactersRead = await stream.ReadAsync(buffer, 0, buffer.Length);

                DisplayResult(buffer: buffer);
        }

        public static async Task ReadWriteAsync(string path)
        {
            Console.WriteLine($"ReadWriteAsync 1 parameters: The thread executing this task is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");
            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            await ReadWriteAsync(path, "");
        }

        private static void DisplayResult(byte[] buffer)
        {
            Console.WriteLine($"DisplayResult: The thread executing this method is {Thread.CurrentThread.Name}, {Thread.CurrentThread.ManagedThreadId}");
            if (Thread.CurrentThread.IsBackground)
            {
                Console.WriteLine($"And it is a background thread.");

            }
            else
            {
                Console.WriteLine("And it is a non-background thread");
            }

            Console.WriteLine();

            if (Thread.CurrentThread.IsThreadPoolThread)
            {
                Console.WriteLine("It is a ThreadPoolThread");
            }
            else
            {
                Console.WriteLine("It is a non-ThreadPoolThread");
            }

            string DecodedText = Encoding.UTF8.GetString(buffer, 0, buffer.Length);

            string[] strings = DecodedText.Split('\n');

            for (int index = 0; index < strings.Length;index++)
            {
                Console.WriteLine(strings[index]);
            }
        }

输出:

Main method: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ProcessReadWriteAsync: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ReadWriteAsync 1 parameters: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ReadWriteAsync 2 parameters: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ProcessReadWriteAsync: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ReadWriteAsync 1 parameters: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
ReadWriteAsync 2 parameters: The thread executing this task is , 1
And it is a non-background thread

It is a non-ThreadPoolThread
DisplayResult: The thread executing this method is , 4
And it is a background thread.

It is a ThreadPoolThread
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                     42972
8. msdn.microsoft.com/library/ff730837.aspx               146159
9. msdn.microsoft.com/library/hh191443.aspx                83732
10. msdn.microsoft.com/library/aa578028.aspx               205273
11. msdn.microsoft.com/library/jj155761.aspx                29019
12. msdn.microsoft.com/library/hh290140.aspx               117152
13. msdn.microsoft.com/library/hh524395.aspx                68959
14. msdn.microsoft.com/library/ms404677.aspx               197325
15. msdn.microsoft.com                                     42972
16. msdn.microsoft.com/library/ff730837.aspx               146159
17. msdn.microsoft.com/library/hh191443.aspx                83732
18. msdn.microsoft.com/library/aa578028.aspx               205273
19. msdn.microsoft.com/library/jj155761.aspx                29019
20. msdn.microsoft.com/library/hh290140.aspx               117152
21. msdn.microsoft.com/library/hh524395.aspx                68959
22. msdn.microsoft.com/library/ms404677.aspx               197325
23. msdn.microsoft.com                                     42972
24. msdn.microsoft.com/library/ff730837.aspx               146159
25. msdn.microsoft.com/library/hh191443.aspx                83732
26. msdn.microsoft.com/library/aa578028.aspx               205273
27. msdn.microsoft.com/library/jj155761.aspx                29019
28. msdn.microsoft.com/library/hh290140.aspx               117152
29. msdn.microsoft.com/library/hh524395.aspx                68959
30. msdn.microsoft.com/library/ms404677.aspx               197325
31. msdn.microsoft.com                                     42972
32. msdn.microsoft.com/library/ff730837.aspx               146159
33. msdn.microsoft.com/library/hh191443.aspx                83732
34. msdn.microsoft.com/library/aa578028.aspx               205273
35. msdn.microsoft.com/library/jj155761.aspx                29019
36. msdn.microsoft.com/library/hh290140.aspx               117152
37. msdn.microsoft.com/library/hh524395.aspx                68959
38. msdn.microsoft.com/library/ms404677.aspx               197325
39. msdn.microsoft.com                                     42972
40. msdn.microsoft.com/library/ff730837.aspx               146159
41. msdn.microsoft.com/library/hh191443.aspx                83732
42. msdn.microsoft.com/library/aa578028.aspx               205273
43. msdn.microsoft.com/library/jj155761.aspx                29019
44. msdn.microsoft.com/library/hh290140.aspx               117152
45. msdn.microsoft.com/library/hh524395.aspx                68959
46. msdn.microsoft.com/library/ms404677.aspx               197325
47. msdn.microsoft.com                                     42972
48. msdn.microsoft.com/library/ff730837.aspx               146159
49. msdn.microsoft.com/library/hh191443.aspx                83732
50. msdn.microsoft.com/library/aa578028.aspx               205273
51. msdn.microsoft.com/library/jj155761.aspx                29019
52. msdn.microsoft.com/library/hh290140.aspx               117152
53. msdn.microsoft.com/library/hh524395.aspx                68959
54. msdn.microsoft.com/library/ms404677.aspx               197325
55. msdn.microsoft.com                                     42972
56. msdn.microsoft.com/library/ff730837.aspx               146159
57. msdn.microsoft.com/library/hh191443.aspx                83732
58. msdn.microsoft.com/library/aa578028.aspx               205273
59. msdn.microsoft.com/library/jj155761.aspx                29019
60. msdn.microsoft.com/library/hh290140.aspx               117152
61. msdn.microsoft.com/library/hh524395.aspx                68959
62. msdn.microsoft.com/library/ms404677.aspx               197325
63. msdn.microsoft.c

DisplayResult: The thread executing this method is , 5
And it is a background thread.

It is a ThreadPoolThread
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                     42972
8. msdn.microsoft.com/library/ff730837.aspx               146159
9. msdn.microsoft.com/library/hh191443.aspx                83732
10. msdn.microsoft.com/library/aa578028.aspx               205273
11. msdn.microsoft.com/library/jj155761.aspx                29019
12. msdn.microsoft.com/library/hh290140.aspx               117152
13. msdn.microsoft.com/library/hh524395.aspx                68959
14. msdn.microsoft.com/library/ms404677.aspx               197325
15. msdn.microsoft.com                                     42972
16. msdn.microsoft.com/library/ff730837.aspx               146159
17. msdn.microsoft.com/library/hh191443.aspx                83732
18. msdn.microsoft.com/library/aa578028.aspx               205273
19. msdn.microsoft.com/library/jj155761.aspx                29019
20. msdn.microsoft.com/library/hh290140.aspx               117152
21. msdn.microsoft.com/library/hh524395.aspx                68959
22. msdn.microsoft.com/library/ms404677.aspx               197325
23. msdn.microsoft.com                                     42972
24. msdn.microsoft.com/library/ff730837.aspx               146159
25. msdn.microsoft.com/library/hh191443.aspx                83732
26. msdn.microsoft.com/library/aa578028.aspx               205273
27. msdn.microsoft.com/library/jj155761.aspx                29019
28. msdn.microsoft.com/library/hh290140.aspx               117152
29. msdn.microsoft.com/library/hh524395.aspx                68959
30. msdn.microsoft.com/library/ms404677.aspx               197325
31. msdn.microsoft.com                                     42972
32. msdn.microsoft.com/library/ff730837.aspx               146159
33. msdn.microsoft.com/library/hh191443.aspx                83732
34. msdn.microsoft.com/library/aa578028.aspx               205273
35. msdn.microsoft.com/library/jj155761.aspx                29019
36. msdn.microsoft.com/library/hh290140.aspx               117152
37. msdn.microsoft.com/library/hh524395.aspx                68959
38. msdn.microsoft.com/library/ms404677.aspx               197325
39. msdn.microsoft.com                                     42972
40. msdn.microsoft.com/library/ff730837.aspx               146159
41. msdn.microsoft.com/library/hh191443.aspx                83732
42. msdn.microsoft.com/library/aa578028.aspx               205273
43. msdn.microsoft.com/library/jj155761.aspx                29019
44. msdn.microsoft.com/library/hh290140.aspx               117152
45. msdn.microsoft.com/library/hh524395.aspx                68959
46. msdn.microsoft.com/library/ms404677.aspx               197325
47. msdn.microsoft.com                                     42972
48. msdn.microsoft.com/library/ff730837.aspx               146159
49. msdn.microsoft.com/library/hh191443.aspx                83732
50. msdn.microsoft.com/library/aa578028.aspx               205273
51. msdn.microsoft.com/library/jj155761.aspx                29019
52. msdn.microsoft.com/library/hh290140.aspx               117152
53. msdn.microsoft.com/library/hh524395.aspx                68959
54. msdn.microsoft.com/library/ms404677.aspx               197325
55. msdn.microsoft.com                                     42972
56. msdn.microsoft.com/library/ff730837.aspx               146159
57. msdn.microsoft.com/library/hh191443.aspx                83732
58. msdn.microsoft.com/library/aa578028.aspx               205273
59. msdn.microsoft.com/library/jj155761.aspx                29019
60. msdn.microsoft.com/library/hh290140.aspx               117152
61. msdn.microsoft.com/library/hh524395.aspx                68959
62. msdn.microsoft.com/library/ms404677.aspx               197325
63. msdn.microsoft.c

所有异步方法都在当前线程上启动 运行。直到你点击一个作用于不完整 Taskawait 之前,没有任何不同发生。但是无论您向 await 提供什么,都必须在 await 实际执行任何操作之前 return 一个值(Task)。

所以让我们来看看到底发生了什么:

  1. Main 运行直到它调用 ProcessReadWriteAsync(@"/tmp/temp1Write.txt")
  2. ProcessReadWriteAsync(string) 运行直到它调用 ReadWriteAsync(filePath)
  3. ReadWriteAsync(string) 运行直到它调用 ReadWriteAsync(path, "")
  4. ReadWriteAsync(string,string) 运行直到它调用 stream.ReadAsync(buffer, 0, buffer.Length)
  5. stream.ReadAsync() 施展魔法,直到 return 成为不完整的 Task
  6. ReadWriteAsync(string,string)中的await看到那个不完整Taskreturns一个新的不完整Task ,该方法的其余部分注册为 Task
  7. 的延续
  8. ReadWriteAsync(string) return一个不完整的 Task
  9. ProcessReadWriteAsync(string) return一个不完整的 Task
  10. Main() 中还没有 await,因此继续执行。
  11. Main() 调用 ProcessReadWriteAsync(@"/tmp/temp2Write.txt"),它再次开始整个过程​​。

所有这些都发生在同一个线程上。

当您最终调用 await task1 时,它会告诉它暂停执行,直到 Task 完成。此时,您的代码 none 不再是 运行。

stream.ReadAsync() 最终完成时,其 Task 设置为 Completed 并且 ReadWriteAsync(string,string) 的其余部分运行直到完成,然后触发 ReadWriteAsync(string) 运行 完成,然后触发 ProcessReadWriteAsync(string) 到 运行 完成。所有这些都发生在后台线程上,这就是为什么您在后台线程上看到 DisplayResult 运行。

一旦 task1 设置为完成,您的 Main() 方法将恢复。

请记住,在具有同步上下文的应用程序中,如 UI 应用程序或 ASP.NET(非 Core),延续不会发生在后台线程上。它们将发生在它们开始的同一个线程上。因此,在这些情况下,直到您点击 await task1,延续才会开始。但是,您可以告诉它您不需要它们 return 到与 .ConfigureAwait(false).

相同的同步上下文