简体   繁体   English

异步程序未异步运行的奇怪行为

[英]Strange behavior with asynchronous program not running asynchronously

I printed which thread executes each called method in the following program and the results are very strange.我在下面的程序中打印了哪个线程执行每个调用的方法,结果很奇怪。 I expected all asynchronous calls to be executed by a threadpool thread because @Jon Skeet mentioned that asynchronous calls use ThreadPool threads and that thread pool threads are background threads.我希望所有异步调用都由线程池线程执行,因为@Jon Skeet 提到异步调用使用 ThreadPool 线程并且线程池线程是后台线程。

The thread id shows that the same thread that executes the Main method executes all called methods except for DisplayResult which is not an asynchronous method.线程 id 显示执行 Main 方法的同一线程执行所有调用的方法,但 DisplayResult 不是异步方法。

How is it that each call of DisplayResult, which is not an asynchronous method, is executed by a threadpool thread where as actual asynchronous methods when called are executed by a non-background thread, the same non-background thread that executed the Main method?不是异步方法的 DisplayResult 的每个调用都是由线程池线程执行的,而实际异步方法在调用时是由非后台线程执行的,即执行 Main 方法的同一个非后台线程? And how many background threads and non-background threads should there be in this program?这个程序应该有多少个后台线程和非后台线程?

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]);
            }
        }

Output: Output:

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

All asynchronous methods start running on the current thread.所有异步方法都开始在当前线程上运行。 Nothing different happens until you hit an await that acts on an incomplete Task .在您点击一个作用于不完整Taskawait之前,不会发生任何不同。 But whatever you're feeding to await has to actually return a value (a Task ) before await actually does anything.但是,无论您要喂什么, await都必须在await实际执行任何操作之前实际返回一个值(一个Task )。

So lets walk through exactly what's happening:所以让我们来看看到底发生了什么:

  1. Main runs until it calls ProcessReadWriteAsync(@"/tmp/temp1Write.txt") Main运行直到它调用ProcessReadWriteAsync(@"/tmp/temp1Write.txt")
  2. ProcessReadWriteAsync(string) runs until it calls ReadWriteAsync(filePath) ProcessReadWriteAsync(string)一直运行,直到它调用ReadWriteAsync(filePath)
  3. ReadWriteAsync(string) runs until it calls ReadWriteAsync(path, "") ReadWriteAsync(string)一直运行直到它调用ReadWriteAsync(path, "")
  4. ReadWriteAsync(string,string) runs until it calls stream.ReadAsync(buffer, 0, buffer.Length) ReadWriteAsync(string,string)一直运行到它调用stream.ReadAsync(buffer, 0, buffer.Length)
  5. stream.ReadAsync() does some magic until it returns an incomplete Task stream.ReadAsync()会做一些魔术,直到它返回一个不完整的Task
  6. The await in ReadWriteAsync(string,string) see that incomplete Task and returns a new incomplete Task , with the rest of the method signed up as a continuation of that Task ReadWriteAsync(string,string)中的await看到不完整的Task返回一个新的不完整的Task ,该方法的 rest 注册为该Task的延续
  7. ReadWriteAsync(string) returns an incomplete Task ReadWriteAsync(string)返回一个不完整的Task
  8. ProcessReadWriteAsync(string) returns an incomplete Task ProcessReadWriteAsync(string)返回一个不完整的Task
  9. There is no await yet in Main() , so execution continues there. Main()中还没有await ,因此在那里继续执行。
  10. Main() calls ProcessReadWriteAsync(@"/tmp/temp2Write.txt") , which starts that whole process again. Main()调用ProcessReadWriteAsync(@"/tmp/temp2Write.txt") ,它再次启动整个过程。

All of that happens on the same thread.所有这些都发生在同一个线程上。

When you finally call await task1 , it tells it to pause execution until the Task is completed.当您最终调用await task1时,它会告诉它暂停执行,直到Task完成。 At this point, none of your code is running anymore.此时,您的任何代码都不再运行。

When stream.ReadAsync() finally completes, its Task is set to Completed and the rest of ReadWriteAsync(string,string) runs until completion, which then triggers ReadWriteAsync(string) to run to completion, which then triggers ProcessReadWriteAsync(string) to run to completion.stream.ReadAsync()最终完成时,其Task设置为CompletedReadWriteAsync(string,string)的 rest 运行直到完成,然后触发ReadWriteAsync(string)运行到完成,然后触发ProcessReadWriteAsync(string)运行完成。 All of this happens on a background thread, which is why you see DisplayResult running on a background thread.所有这些都发生在后台线程上,这就是您看到DisplayResult在后台线程上运行的原因。

Once task1 is set to completed, your Main() method resumes.一旦task1设置为完成,您的Main()方法就会恢复。

Keep in mind that in an application with a synchronization context, like a UI application or ASP.NET (not Core), the continuations do not happen on a background thread.请记住,在具有同步上下文的应用程序中,例如 UI 应用程序或 ASP.NET(不是核心),继续不会发生在后台线程上。 They will happen on the same thread they started on.它们将发生在它们开始的同一线程上。 So in those cases, the continuations would not even start until you hit await task1 .因此,在这些情况下,在您点击await task1之前,延续甚至不会开始。 However, you can tell it that you don't need them to return to the same synchronization context with .ConfigureAwait(false) .但是,您可以告诉它您不需要它们返回到与.ConfigureAwait(false)相同的同步上下文。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM