C# 使用StreamReader.ReadToEndAsync()等待不会按预期返回调用方
试图理解流和C# 使用StreamReader.ReadToEndAsync()等待不会按预期返回调用方,c#,.net,asynchronous,C#,.net,Asynchronous,试图理解流和异步/等待。如果我正确理解wait将执行返回给调用者,因此我预期以下代码的结果为: before calling async ReadToEnd after the call to ReadToEnd before calling async ReadToEnd after the call to ReadToEnd 但事实并非如此 before calling async ReadToEnd after the call to ReadToEnd before calling a
异步/等待
。如果我正确理解wait
将执行返回给调用者,因此我预期以下代码的结果为:
before calling async ReadToEnd
after the call to ReadToEnd
before calling async ReadToEnd
after the call to ReadToEnd
但事实并非如此
before calling async ReadToEnd
after the call to ReadToEnd
before calling async ReadToEnd
after await in ReadToEnd. streamReader.BaseStream.GetType(): System.IO.MemoryStream
after the call to ReadToEnd
似乎在调用StreamReader.ReadToEndAsync()
时,将StreamReader
与下面的FileStream一起使用StreamReader
确实会返回调用方,但在将streamreadReader
与下面的MemoryStream一起使用时,它不起作用
为了理解发生了什么,我阅读了.NET源代码,并得出结论:使用下面的内存流调用StreamReadReader.ReadToEndAsync()
,最终会调用基流上的ReadAsync()
()。在MemoryStream
的情况下,ReadAsync()
实际上不是一个asynhnous方法,因此不会返回给调用方
我的理解正确吗
using System;
using System.Text;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
static class Program
{
public static void Main(string[] args)
{
var longString = new string(Enumerable.Repeat('x', 100000000).ToArray());
File.WriteAllText("bigFile.txt", longString, Encoding.UTF32);
StreamReader fileStreamReader = new StreamReader(File.OpenRead(@"bigFile.txt"));
Console.WriteLine("before calling async ReadToEnd");
var task = ReadToEnd(fileStreamReader);
Console.WriteLine("after the call to ReadToEnd");
byte[] bytes = Encoding.UTF32.GetBytes(longString);
StreamReader memoryStreamReader = new StreamReader(new MemoryStream(bytes));
Console.WriteLine("before calling async ReadToEnd");
var task2 = ReadToEnd(memoryStreamReader);
Console.WriteLine("after the call to ReadToEnd");
fileStreamReader.Dispose();
memoryStreamReader.Dispose();
}
static async Task ReadToEnd(StreamReader streamReader)
{
string allText = await streamReader.ReadToEndAsync();
Console.WriteLine("after await in ReadToEnd. streamReader.BaseStream.GetType(): " + streamReader.BaseStream.GetType());
}
}
在代码的第一部分中,您将从文件创建StreamReader
。调用streamReader.ReadToEndAsync()
时,必须先将文件从硬盘读取到内存中,然后才能返回文件内容,这是异步完成的
在代码的第二部分中,您将从MemoryStream
创建StreamReader
,它是从已加载到应用程序内存中的字节构建的。在这种情况下,streamReader.ReadToEndAsync()
会立即返回内容,因为数据在内存中,可以立即获取。await
仅在等待的事情没有同步完成时才将执行返回给调用方。是的,async
方法如果选择,可以同步完成,因为:
- 他们已经知道答案(缓存结果、缓冲数据、已知故障状态等)
- 它们根本没有合适的异步下游操作来执行
MemoryStream
总是同步的,所以它总是这样做
相比之下,FileStream
可能同步完成,也可能不同步完成,这取决于缓冲区中可用的数据等
关于是否返回调用者的决定是由GetAwaiter().IsCompleted
驱动的,它(对于Task
)归结为。IsCompleted
我想是的,但是MemoryStream足够快,可以立即返回。因此,您会看到额外的Console.WriteLine出现在预期的Console.WriteLine之前。在wait和Console.WriteLine之间添加一个Sleep,看看会发生什么…@AroglDarthu这实际上不是一个“足够快”的问题——它根本不是异步的;方法可以是以下任意一种:慢速和同步、慢速和异步、快速和同步、快速和异步。快与慢是没有意义的-只有同步与异步才是重要的(在合理的范围内;显然,如果它是异步的,并且速度足够快,可以击败ret
指令,那么……好吧,也许吧;但这是一个非常边缘的情况,不重要)@MarcGravel感谢您的见解。@inwenis:您可能会发现我的帮助。总之,wait
首先检查其操作是否完成,如果完成,则继续同步执行。