0

Excuse my ignorance, but I am having problems understanding the MSDN excerpt for reading file contents asynchronously

https://msdn.microsoft.com/en-us/library/jj155757.aspx

string text = await ReadTextAsync(filePath);

...

private async Task<string> ReadTextAsync(string filePath)
{
    using (FileStream sourceStream = new FileStream(filePath,
        FileMode.Open, FileAccess.Read, FileShare.Read,
        bufferSize: 4096, useAsync: true))
    {
        StringBuilder sb = new StringBuilder();

        byte[] buffer = new byte[0x1000];
        int numRead;
        while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
        {
            string text = Encoding.Unicode.GetString(buffer, 0, numRead);
            sb.Append(text);
        }

        return sb.ToString();
    }
}

How is that reading asynchronously? It appears to wait until ReadTextAsync has completed to return the contents. If there were a Thread.Sleep inserted inside that method, then it would wait to complete, and no further code would run, after the call to the method.

Andrey Ermakov
  • 3,298
  • 1
  • 25
  • 46
tic
  • 2,484
  • 1
  • 21
  • 33
  • It's asynchronous because of the `await sourceStream.ReadAsync`. Note that asynchronous doesn't mean you don't wait, it means you don't tie up a thread while you wait, if you choose to wait. – juharr Feb 26 '16 at 20:24
  • How do you actually read the entirety of the file contents asynchronously then? You can't call that method and continue with program execution – tic Feb 26 '16 at 20:25
  • *This* method "waits" for the response, but the consuming system is free to do other things while this task completes. `"You can't call that method and continue with program execution"` - You most certainly can, and that's exactly what the system does. For example, when the UI continues to render and respond while the method executes. That's the application continuing to do things. – David Feb 26 '16 at 20:25
  • There is huge difference between "wait" and `await`. These http://stackoverflow.com/questions/9519414/whats-the-difference-between-task-start-wait-and-async-await, http://stackoverflow.com/questions/14455293/how-to-and-when-use-async-and-await may help (duplicate of missing knowledge, not really related to question as asked). – Alexei Levenkov Feb 26 '16 at 20:27
  • 1
    I don't think I should try to submit a full answer about it, but you may be confusing the boundary between "an asynchronous method" and "a concurrent operation." – clarkitect Feb 26 '16 at 20:28
  • @David If I add `Thread.Sleep(10000)` into the beginning of the `ReadTextAsync`, then the next line after calling that method won't execute until 10 seconds have passed. How could I get round that and check the result of the task later? Still keeping it a separate contained method. – tic Feb 26 '16 at 23:20
  • @tic: Sleeping the thread tends to pause execution, always has. Take a look at Task.Delay() as an alternative for async. – David Feb 26 '16 at 23:41

2 Answers2

1

In a nut shell await makes sure that for I/O operations (like database queries, file reads, network writes, etc) that the thread is released back to the O/S while the code waits for the result on a completion port. This way your threads or not sitting around indling waiting on I/O to complete, now they can be reused by the O/S for other activities. Once the I/O completes the O/S will reassign a new thread OR use the original thread (depending on ConfigureAwait) to resume the operation.

So this is not parallel or concurrent programming, it is simply allowing the thread to service other requests while a process (code point) waits on I/O to complete.

Also see Asynchronous Programming with Async and Await for additional details.

Igor
  • 60,821
  • 10
  • 100
  • 175
  • Ah, that does explain it. I should add then to my question, how should I have a method that can be called concurrently to read a file's contents, and continue execution? – tic Feb 26 '16 at 20:40
  • @tick - That is a very different thing indeed :) I guess you would need to split the file length into start/end ranges, assign multiple threads each a range, and open a FileStream as readonly with no lock, just read their range, and have some type of final process that puts it all back together again. That said you will probably see 0 benefit because you are reading from the same disk and the O/S can only service one read from the same file at a time so your bottle neck will still be the I/O. This would probably not be worth the effort to create. – Igor Feb 26 '16 at 20:44
0

The problem was with a lack of understanding from myself about the async modifier. The original example is running asynchronously, but any extra slow completing code added into the method, would run synchronously and wait until the calling thread had completed. If a Sleep was really wanted then the entire call could be wrapped in a new Task and called, returning the task and not the result.

This does not pause program execution when you are reading from an external drive for example.

    public Task<string> ReadTextAsync(string filePath)
    {
        var task = new Task<string>(() =>
        {
            Thread.Sleep(5000);
            using (FileStream sourceStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true))
            {
                StringBuilder sb = new StringBuilder();

                byte[] buffer = new byte[0x1000];
                int numRead;
                while ((numRead = sourceStream.Read(buffer, 0, buffer.Length)) != 0)
                {
                    string text = Encoding.ASCII.GetString(buffer, 0, numRead);
                    sb.Append(text);
                }

                return sb.ToString();
            }
        });
        task.Start();
        return task;
    }
tic
  • 2,484
  • 1
  • 21
  • 33