简体   繁体   English

Infinite Do ... while循环使用Async Await

[英]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. 我试图读取Stream以异步方式写入MemoryStream ,但Do...while循环未能中断。 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. 其次,如果流实际上没有结束 ,那么bytesRead永远不会为零。 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. 我怀疑是这种情况,因为方法的名称( ReadLineAsync )并不意味着它将在流结束之前读取。

PS CanRead does not ever change for a specific stream. PS CanRead不会针对特定流进行更改。 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. 只要CanRead为true且bytesRead大于0,就将循环条件设置为运行。如果文件可读,CanRead将始终为true。 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++) 我是(在Notepad ++中用Windows-1252编码)

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. 请注意如何按预期打印“按ENTER继续...”,因为主方法是异步调用的,并且CanRead始终为true,因为这意味着文件是可读的。 Its the state of how the file was opened, not the state meaning that the cursor is at the EOF. 它是文件打开方式的状态,而不是状态意味着光标位于EOF。

So, you are taking a stream from IMAP and this method is for converting that steam into text? 那么,你是从IMAP获取一个流,这个方法是将这个流转换为文本?
Why not construct a SteamReader round the stream and call either it's ReadToEndAsync or just ReadToEnd? 为什么不围绕流构建一个SteamReader并调用它的ReadToEndAsync或只调用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. 我怀疑是否需要将其作为异步操作,如果流类似于电子邮件,那么用户在读取时会注意到UI阻塞的可能性不大。
If, as one of your comments suggests, this isn't a UI app at all then it is probably even less of an issue. 如果你的评论之一表明,这根本不是一个UI应用程序,那么它可能更不是一个问题。

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. 编辑:我刚刚注意到你的方法被称为ReadLineAsync,虽然我无法在代码中看到你正在寻找行结尾的任何地方。 If your intention is to read a line of text then the SteamReader also provides ReadLine and ReadLineAsync. 如果您打算阅读一行文本,那么SteamReader还提供ReadLine和ReadLineAsync。

From my POV, looks like your code is trying to do the following: 从我的POV看起来,您的代码正在尝试执行以下操作:

  • read an entire stream as a sequence of 1024-octet chunks, 将整个流读取为1024个八位字节块的序列,
  • concatenate all those chunks into a MemoryStream (which uses a byte array as its backing store), 将所有这些块连接到MemoryStream (使用字节数组作为其后备存储),
  • convert the MemoryStream to a string using the specified encoding 使用指定的编码将MemoryStream转换为字符串
  • 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. 也许我错过了一些东西,但是要使用asyncawait ,你必须使用VS2012和.Net 4.5或VS2010。 .Net 4.0 and the Async CTP, right? .Net 4.0和Async CTP,对吗? If so, why wouldn't you simply use a StreamReader and its StreamReader.ReadToEndAsync() method? 如果是这样,为什么不简单地使用StreamReader及其StreamReader.ReadToEndAsync()方法?

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). 重叠的i / o想法很不错,但是写入内存流所需的时间至少可以说是不足以在执行实际I / O所需的时间之间产生差异(可能是你输入的内容)流正在做磁盘或网络i / o)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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