0

I have a hopefully quick question. I'm trying to get to grips with asynchronous code and am wondering about best practices or best utilization of it. So, if you have some code such as

public async void DoStuff()
{
   await Task.Factory.StartNew(() => { LoadFile(); });
   DoSomethingAfter();
}

public void LoadFile()
{
   StreamReader sr = new StreamReader("file.txt");
   String line;
   while ((line = sr.ReadLine()) != null) 
   {
      switch(line)
      {
         case "A":
           parseObjectA(line, sr); //continues with its own loop
           break;
         case "B":
           parseObjectB(line, sr); //continues with its own loop
           break;
      }
   }
}

//added for clarity
public Object parseObjectA(String line, StreamReader sr)
{
   Object obj = new Object();
   while ((line = sr.ReadLine()) != null) 
   {
      String element;
      String value;
      parseLine(line, out element, out value);
      switch(element)
      {
         case "name":
            obj.Name = value;
            break;
         case "position":
            {
               int pos = 0;
               Int32.TryParse(value, out pos);
               obj.position = pos;
               break;
            }
      }
   }
   return obj;
}

Is the StreamReader loop blocking the task I set up? I noticed it's taking way longer to read the files I'm sending to it than when I didn't use a task. Do the functions need to be async all the way down? Like, would the things happening in parseObject also need to be async, and the file reading need to be async? Thanks.

DangerMcD
  • 21
  • 1
  • 7
  • 3
    You're better using `await sr.ReadLineAsync` than spinning up an explicit compute-bound `Task` via `StartNew()` –  Nov 16 '17 at 07:26
  • _"wondering about best practices or best utilization of it"_ I have bookmarked these links, I hope you'll find them as informative as I did: [best practices for the busy developer](https://scalablenotions.wordpress.com/2015/05/02/tpl-and-async-await-best-practices-for-the-busy-developer/#dont-do-it), [MSDN Magazine](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx) and [Stephen C's blog - Task.Run etiquette](https://blog.stephencleary.com/2013/10/taskrun-etiquette-and-proper-usage.html) – Fildor Nov 16 '17 at 07:46
  • @MickyD what if LoadFiles does more than just read the files? or if there's another function in the task that gets called when LoadFile() is done? should there be async calls all the way down to get the best usage out of threading? – DangerMcD Nov 16 '17 at 07:55
  • I'm going purely by your example. If you have another scenario post another question or update the above –  Nov 16 '17 at 07:59
  • @Fildor Thanks! i actually came across that msdn one in my search for this answer, hence the asking if streamreader would block and doesn't count as "all the way down". Do you know if it does block it, or should I switch all my data reading to ReadLineAsync like MickyD said? – DangerMcD Nov 16 '17 at 08:00
  • @MickyD This is still the same scenario, I'm asking if streamreader blocks it, and then if all consecutive calls, like the parseObject functions inside of the reader loop, need some degree of async calls or return tasks of their own. – DangerMcD Nov 16 '17 at 08:03
  • @DangerMcD What you've got here is a mix of I/O-Bound Operation and CPU-Bound operation. I am afraid I haven't got the link at hand, but I know I have read a blog post from Stephen C about exactly that. For the I/O-bound part you should definitely use async APIs "all the way". – Fildor Nov 16 '17 at 08:12
  • I only found these: https://learn.microsoft.com/en-us/dotnet/standard/async-in-depth , and maybe also related : https://stackoverflow.com/a/14902702/982149 – Fildor Nov 16 '17 at 08:17
  • 1
    `await ReadLineAsync` does not block. It does not even use a thread! –  Nov 16 '17 at 08:20
  • @Fildor great reading material! I'll be sure to go over these, thanks! – DangerMcD Nov 16 '17 at 08:21
  • @MickyD `ReadLineAsync` does use a thread (it has to perform multiple reads and process data to combine a line) and might even block if you are reading from something like `MemoryStream`, because implementation of `ReadAsync` on MemoryStream is completely synchronous. – Evk Nov 16 '17 at 08:34
  • @Evk _[There Is No Thread](https://blog.stephencleary.com/2013/11/there-is-no-thread.html)_ –  Nov 16 '17 at 09:10
  • @MickyD yes I know, but in this case there is thread (because ReadLineAsync does not perform _only_ async IO). – Evk Nov 16 '17 at 09:11
  • @Evk Actually, I don't think that matters much in this case, since OP has I/O and CPU bound operations mixed anyway. What I took away from "There is no Thread" is that OP should use async/await on async APIs wherever possible *and* use `Task.Run` to call his `DoStuff` instead of TaskFactory.StartNew inside of it. What do you think? – Fildor Nov 16 '17 at 13:08
  • @Fildor I went over some of the materials you linked me and I'm a little confused on how I would go about separating I/O and CPU operations for the purposes of using async Tasks. I edited the code in the question for some clarity on what parseObjectA would do, but from what I understand of synchronous file reading, I read in lines and then parse them into whatever I want. Are you suggesting I decouple these two processes when I switch to asynchronous file reading? – DangerMcD Nov 16 '17 at 19:15
  • *"I noticed it's taking way longer to read the files I'm sending to it than when I didn't use a task."* that is because you used `public async void DoStuff()`, it is quicker because you are not waiting for the task to finish, It is still taking the same amount of time but you now have no way to tell when it is done. – Scott Chamberlain Nov 16 '17 at 20:11
  • @ScottChamberlain It's not in the pseudocode but I use a state based system in the main loop. Three states, Main, Loading, Done. When the file reading is called, it switches to loading and a loading screen pops up. When the files are done being read, it switches to Done and the new objects are processed. Then it switches back to main and the program continues. The file reading without the async tasks takes 1.5 seconds, while with the async reading, it takes 53 seconds. Same code, just wrapped in a task. – DangerMcD Nov 16 '17 at 20:21

1 Answers1

1

Use ReadAsync on FileStream to read async file

public async Task<byte[]> ReadFileAsync(string path)
{
        using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read,FileShare.Read))
        {
            var result = new byte[fileStream.Length];
            await fileStream.ReadAsync(result, 0, (int)fileStream.Length);
            return result;
        }
}
Mak Ahmed
  • 578
  • 5
  • 16