[英]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
.在您点击一个作用于不完整Task
的await
之前,不会发生任何不同。 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:所以让我们来看看到底发生了什么:
Main
runs until it calls ProcessReadWriteAsync(@"/tmp/temp1Write.txt")
Main
运行直到它调用ProcessReadWriteAsync(@"/tmp/temp1Write.txt")
ProcessReadWriteAsync(string)
runs until it calls ReadWriteAsync(filePath)
ProcessReadWriteAsync(string)
一直运行,直到它调用ReadWriteAsync(filePath)
ReadWriteAsync(string)
runs until it calls ReadWriteAsync(path, "")
ReadWriteAsync(string)
一直运行直到它调用ReadWriteAsync(path, "")
ReadWriteAsync(string,string)
runs until it calls stream.ReadAsync(buffer, 0, buffer.Length)
ReadWriteAsync(string,string)
一直运行到它调用stream.ReadAsync(buffer, 0, buffer.Length)
stream.ReadAsync()
does some magic until it returns an incomplete Task
stream.ReadAsync()
会做一些魔术,直到它返回一个不完整的Task
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
的延续ReadWriteAsync(string)
returns an incomplete Task
ReadWriteAsync(string)
返回一个不完整的Task
ProcessReadWriteAsync(string)
returns an incomplete Task
ProcessReadWriteAsync(string)
返回一个不完整的Task
await
yet in Main()
, so execution continues there. Main()
中还没有await
,因此在那里继续执行。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
设置为Completed
, ReadWriteAsync(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.