Reputation: 409
Trying to understand streams and async/await
. If I undertand correctly await
returns execution to the caller so I expected the result of the code below to be:
before calling async ReadToEnd
after the call to ReadToEnd
before calling async ReadToEnd
after the call to ReadToEnd
But insted it is
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
It seems that using a StreamReader
with a FileStream
underneath really returns to the caller when calling StreamReader.ReadToEndAsync()
, but it does not work when using a StreamReader
with a MemoryStream
underneath.
Trying to understand what is going on I've read the .NET source code and came to the conculssion that calling StreamReader.ReadToEndAsync()
with a MemoryStream
underneath eventually calls ReadAsync()
(source) on the BaseStream. In case of a MemoryStream
the ReadAsync()
is just not really a asynhnornous method and thus does not return to the caller.
Is my understanding correct?
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());
}
}
Upvotes: 3
Views: 3845
Reputation: 1062530
await
only returns execution to the caller if the thing being awaited did not complete synchronously. Yes, async
methods can complete synchronously if they choose, either because:
MemoryStream
is always synchronous, so it always does this.
FileStream
, by contrast, may or may not complete synchronously, depending on what data is available in buffers, etc.
The decision as to whether to go back to the caller is driven by GetAwaiter().IsCompleted
, which (for Task
) comes down to .IsCompleted
.
Upvotes: 8
Reputation: 913
In the first part of your code you are creating StreamReader
from File
. When you call streamReader.ReadToEndAsync()
the file must be read first to memory from the hard drive before it contents can be returned which is done asynchronously.
In the second part of your code you are creating StreamReader
from MemoryStream
which was built from bytes
that have been already loaded in memory of the application. In this case the streamReader.ReadToEndAsync()
returns the contents immediately as the data is in memory and can be fetched right away.
Upvotes: 2