简体   繁体   中英

Infinite Do…while loop using Async Await

I have 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 Stream to write into MemoryStream asynchronously, but the Do...while loop is failing to break. I mean it's an infinite loop. How to solve this?

First, in an exceptional situation, your loop would continue indefinitely. You shouldn't catch and ignore exceptions.

Secondly, if the stream doesn't actually end , then bytesRead would never be zero. I suspect this is the case because the name of the method ( ReadLineAsync ) doesn't imply to me that it will read until the end of the stream.

PS CanRead does not ever change for a specific stream. It's whether it makes semantic sense for a stream to do a read operation, not whether it can read right now .

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

I took your method and modified it just a tad by shortening the read buffer size 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 with 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 could it be something with your input file? Mine was (encoded in Windows-1252 in Notepad++)

one
two
three

and my output 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 how the "Press ENTER to continue..." was printed first as expected because the main method was invoked asynchronously, and CanRead is always true because it means the file is readable. Its the state of how the file was opened, not the state meaning that the cursor is at the EOF.

So, you are taking a stream from IMAP and this method is for converting that steam into text?
Why not construct a SteamReader round the stream and call either it's ReadToEndAsync or just ReadToEnd? I doubt the need for making this an Async operation, if the stream is something like an e-mail then it is unlikely to be so big that a user will notice the UI blocking while it reads.
If, as one of your comments suggests, this isn't a UI app at all then it is probably even less of an issue.

If my assumptions are wrong then could I ask you to update your question with some more information about how this function is being 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 can't see anywhere in the code that you are looking for a line ending. If your intention is to read a line of text then the SteamReader also provides ReadLine and ReadLineAsync.

From my POV, looks like your code is trying to do the following:

  • read an entire stream as a sequence of 1024-octet chunks,
  • concatenate all those chunks into a MemoryStream (which uses a byte array as its backing store),
  • convert the MemoryStream to a string using the specified encoding
  • return that string to the caller.

This seems...complicated to me. Maybe I'm missing something, but to use async and await , you've got to be using VS2012 and .Net 4.5, or VS2010. .Net 4.0 and the Async CTP, right? If so, why wouldn't you simply use a StreamReader 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 overlapping i/o idea is nice, but the time required to write to a memory stream is, to say the least, not enough to make one whit of difference with respect to the time required to peform actual I/O (presumably your input stream is doing disk or network i/o).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM