Endless Do ... while loop using Async Await

I have the following code:

public static async Task<string> ReadLineAsync(this Stream stream, Encoding encoding) { byte[] byteArray = null; using (MemoryStream ms = new MemoryStream()) { int bytesRead= 0; do { byte[] buf = new byte[1024]; try { bytesRead = await stream.ReadAsync(buf, 0, 1024); await ms.WriteAsync(buf, 0, bytesRead); } catch (Exception e) { Console.WriteLine(e.Message + e.StackTrace); } } while (stream.CanRead && bytesRead> 0); byteArray = ms.ToArray(); return encoding.GetString(ms.ToArray()); } 

I am trying to read a Stream to write to a MemoryStream asynchronously, but the Do...while not working. I mean an infinite loop. How to solve this?

+6
source share
5 answers

First, in an exceptional situation, your cycle will continue indefinitely. You should not catch and ignore exceptions.

Secondly, if the stream does not actually end, then bytesRead will never be zero. I suspect this is because the method name ( ReadLineAsync ) does not mean that it will read until the end of the stream.

PS CanRead never changes for a specific thread. This makes semantic sense for the stream to perform a read operation, and can it read right now.

+5
source

You have set the loop condition as long as CanRead is true and bytesRead is greater than 0. CanRead will always be true if your file is readable. This means that while you start reading, your bytes will always be greater than zero. You must have the maximum number of bytes to read, as well as a minimum, or set some other control to break.

0
source

I took your method and changed it just a little, reducing the size of the read buffer and adding some debug statements

  public static async Task<string> ReadLineAsync(this Stream stream, Encoding encoding) { const int count = 2; byte[] byteArray = Enumerable.Empty<byte>().ToArray(); using (MemoryStream ms = new MemoryStream()) { int bytesRead = 0; do { byte[] buf = new byte[count]; try { bytesRead = await stream.ReadAsync(buf, 0, count); await ms.WriteAsync(buf, 0, bytesRead); Console.WriteLine("{0:ffffff}:{1}:{2}",DateTime.Now, stream.CanRead, bytesRead); } catch (Exception e) { Console.WriteLine(e.Message + e.StackTrace); } } while (stream.CanRead && bytesRead > 0); byteArray = ms.ToArray(); return encoding.GetString(byteArray); } } 

but basically it worked, as expected, from the following call:

  private static void Main(string[] args) { FileStream stream = File.OpenRead(@"C:\in.txt"); Encoding encoding = Encoding.GetEncoding(1252); Task<string> result = stream.ReadLineAsync(encoding); result.ContinueWith(o => { Console.Write(o.Result); stream.Dispose(); }); Console.WriteLine("Press ENTER to continue..."); Console.ReadLine(); } 

so I'm wondering if this could be something with your input file? Mine was (encoded in Windows-1252 in Notepad ++)

 one two three 

and my result was

 Press ENTER to continue... 869993:True:2 875993:True:2 875993:True:2 875993:True:2 875993:True:2 875993:True:2 875993:True:2 875993:True:1 875993:True:0 one two three 

note that โ€œPress ENTER to continue ...โ€ was printed first, as expected, because the main method was called asynchronously, and CanRead always true because it means the file is readable. This is a file open state, not a state meaning that the cursor is in EOF.

0
source

So, you take the stream from IMAP, and this method is designed to convert this pair to text?
Why not create a SteamReader around the stream and call either its ReadToEndAsync or just ReadToEnd? I doubt the need to do this Async operation, if the stream is like email, then it is unlikely that the user will notice the user interface lock while reading.
If, as one of your comments suggests, this is not a user interface application at all, it is probably even less.

If my assumptions are wrong, I may ask you to update your question with additional information about how this function is used. The more information you can tell us, the better our answers can be.

EDIT: I just noticed that your method is called ReadLineAsync, although I do not see anywhere in the code that you are looking for to end the line. If you intend to read a line of text, then SteamReader also provides ReadLine and ReadLineAsync.

0
source

From my POV, it seems your code is trying to do the following:

  • read the whole stream as a sequence of 1024-octet fragments,
  • combine all these fragments into a MemoryStream (which uses a byte array as storage),
  • convert a MemoryStream to a string using the specified encoding
  • returns this string to the caller.

It seems complicated to me. Maybe something is missing for me, but to use async and await you should use VS2012 and .Net 4.5 or VS2010..Net 4.0 and Async CTP, right? If so, why don't you just use the StreamReader method and its StreamReader.ReadToEndAsync() method?

 public static async Task<string> MyReadLineAsync(this Stream stream, Encoding encoding) { using ( StreamReader reader = new StreamReader( stream , encoding ) ) { return await reader.ReadToEndAsync() ; } } 

The idea of โ€‹โ€‹overlapping I / O is good, but the time it takes to write to the memory stream is at least insufficient to make one difference from the time it takes to form the actual I / O (presumably your input stream is making disk or network input /output).

0
source

Source: https://habr.com/ru/post/946433/


All Articles