6

我有以下代码:

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循环未能中断。我的意思是这是一个无限循环。如何解决这个问题?

4

5 回答 5

5

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

其次,如果流实际上没有end,那么bytesRead永远不会为零。我怀疑是这种情况,因为方法 ( ReadLineAsync) 的名称并不意味着它会一直读取到流结束。

对于特定的流, PSCanRead永远不会改变。流执行读取操作是否具有语义意义,而不是它是否可以立即读取。

于 2013-06-03T18:06:18.880 回答
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 继续...”是如何按预期首先打印的,因为 main 方法是异步调用的,并且CanRead总是正确的,因为这意味着文件是可读的。它是文件打开方式的状态,而不是表示光标位于 EOF 的状态。

于 2013-06-03T18:35:23.260 回答
0

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

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

这似乎……对我来说很复杂。也许我遗漏了一些东西,但要使用asyncand await,你必须使用 VS2012 和 .Net 4.5 或 VS2010。.Net 4.0 和 Async CTP,对吧?如果是这样,您为什么不简单地使用 aStreamReader及其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)。

于 2013-06-03T19:26:43.400 回答
0

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

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

编辑:我刚刚注意到您的方法称为 ReadLineAsync,尽管我在代码中看不到您正在寻找行尾的任何地方。如果您的意图是读取一行文本,则 SteamReader 还提供 ReadLine 和 ReadLineAsync。

于 2013-06-03T18:51:59.423 回答
0

只要 CanRead 为真并且 bytesRead 大于 0,您就可以将循环条件设置为运行。如果您的文件可读,CanRead 将始终为真。这意味着只要您开始读取您的字节将始终大于零。您需要读取最大字节数以及最小字节数或设置一些其他控件以中断。

于 2013-06-03T17:54:25.337 回答