Async minutiae: EndOfStream

Spread the love

Consider the following method to read all text of a file asynchronously. Do you see any issues?

        public static async Task<string> ReadAllTextAsync(string path)
        {
            StringBuilder text = new StringBuilder();
            const int Size = 4096;
            Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, Size, true);
            using (StreamReader reader = new StreamReader(stream))
            {
                char[] buffer = new char[Size / 2];

                while (!reader.EndOfStream)
                {
                    int count = await reader.ReadAsync(buffer, 0, Size / 2);
                    text.Append(buffer, 0, count);
                }
            }

            return text.ToString();
        }

The code is perfectly functional and produces the correct result. There are no obvious performance issues (other than perhaps some tuning of the buffer sizes). But look closer at that while loop:

while (!reader.EndOfStream)
{
    int count = await reader.ReadAsync(buffer, 0, Size / 2);
    text.Append(buffer, 0, count);
}

It turns out the EndOfStream check is redundant. To detect the end of a stream, you must read it. And since the code is already reading the stream anyway, there is a more efficient way to write this:

int count = 0;
do
{
    count = await reader.ReadAsync(buffer, 0, Size / 2);
    if (count != 0)
    {
        text.Append(buffer, 0, count);
    }
}
while (count != 0);

Instead, we simply read until we get back zero characters. If your last read read nothing, you are by definition at the end of stream.

Does this really matter in practice? Probably not much if you’re just reading a local file. However, since EndOfStream is a property, it is not async. And that means there is a chance that you could end up blocking the caller before they have a chance to yield.

This kind of minutia is always seen as pedantic and barely worth the effort — until it isn’t. In any case, it doesn’t hurt to keep it in mind the next time you read.

One thought on “Async minutiae: EndOfStream

Leave a Reply to tobi Cancel reply

Your email address will not be published. Required fields are marked *