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

时间:2013-06-03 17:49:38

标签: c# async-await

我有以下代码:

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());
}

我试图读取Stream以异步方式写入MemoryStream,但Do...while循环未能中断。我的意思是它是一个无限循环。怎么解决这个问题?

5 个答案:

答案 0 :(得分:5)

首先,在特殊情况下,您的循环将无限期地继续。你不应该抓住并忽略异常。

其次,如果流实际上结束,则bytesRead永远不会为零。我怀疑是这种情况,因为方法的名称(ReadLineAsync)并不意味着它会在流结束之前读取。

P.S。对于特定的流,CanRead不会更改。它是否对流进行读取操作具有语义意义,而不是它是否可以立即读取

答案 1 :(得分:0)

只要CanRead为true且bytesRead大于0,就将循环条件设置为运行。如果文件可读,CanRead将始终为true。这意味着只要您开始读取您的字节将始终大于零。您需要具有要读取的最大字节数以及最小值,或者设置一些其他控件才能突破。

答案 2 :(得分:0)

我采用你的方法并通过缩短读缓冲区大小并添加一些调试语句来修改它

    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);
        }
    }

但基本上它按预期工作,并进行以下调用:

    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();
    }

所以我想知道它可能与你的输入文件有关吗?我是(在Notepad ++中用Windows-1252编码)

one
two
three

我的输出是

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

请注意首先按预期打印“按ENTER继续...”,因为主方法是异步调用的,CanRead始终为true,因为这意味着文件是可读的。它是文件打开方式的状态,而不是状态意味着光标位于EOF。

答案 3 :(得分:0)

所以,你是从IMAP获取一个流,这个方法是将这个流转换为文本?
为什么不围绕流构建一个SteamReader并调用它的ReadToEndAsync或只调用ReadToEnd?我怀疑是否需要将其作为异步操作,如果流类似于电子邮件,那么用户在读取时会注意到UI阻塞的可能性不大。 如果你的评论之一表明,这根本不是一个UI应用程序,那么它可能就不那么重要了。

如果我的假设是错误的,那么我可以请求您更新您的问题,并提供有关如何使用此功能的更多信息。您可以告诉我们的信息越多,我们的答案就越好。

编辑: 我只是注意到你的方法被称为ReadLineAsync,虽然我在代码中看不到你正在寻找行结尾的任何地方。如果您打算阅读一行文本,那么SteamReader还提供ReadLine和ReadLineAsync。

答案 4 :(得分:0)

从我的POV看起来,您的代码正在尝试执行以下操作:

  • 将整个流读取为1024个八位字节块的序列,
  • 将所有这些块连接成MemoryStream(使用字节数组作为其后备存储),
  • 使用指定的编码将MemoryStream转换为字符串
  • 将该字符串返回给调用者。

这对我来说似乎很复杂。也许我错过了一些东西,但要使用asyncawait,你必须使用VS2012和.Net 4.5或VS2010。 .Net 4.0和Async CTP,对吗?如果是这样,为什么不简单地使用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() ;
  }
}

重叠的i / o想法很不错,但写入内存流所需的时间,至少可以说,不足以在执行实际I / O所需的时间上产生一点差异(大概你的输入流正在做磁盘或网络i / o)。