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
首先检查其操作是否完成,如果完成,则继续同步执行。