2013-06-03 11 views
6

を使用している:私は非同期MemoryStreamに書き込むStreamを読みしようとしていますが、Do...whileループを破るために失敗している無限ループない...私は次のコードしている非同期待つ

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

。私はそれが無限ループであることを意味します。これを解決するには?

+1

デバッガで試しましたか? –

+1

UIコンテキストでこのメソッドから生成されたタスクで 'Wait'または' Result'を呼び出していますか?もしそうなら、デッドロックが発生します。 – Servy

+2

あなたはストリームの最初の1024バイトを毎回読み込み、次のバイトセットに移動していませんか? –

答えて

0

CanReadがtrueで、bytesReadが0より大きい限り、ループ条件が実行されるように設定されています。CanReadは、ファイルが読み取り可能であれば常にtrueになります。つまり、読み込みを開始する限り、バイトは常にゼロより大きくなります。読み込みには最大バイト数を必要とし、最小値を設定するか、他のコントロールを設定する必要があります。

+0

で議論されているとおりです。私はStream.CanReadはその目的のためだけだと思います。私たちは手動でそれを行う必要はありません –

+0

あなたは正しく私はそれを正しく言わなかった。私はそれを編集しました。 – MichelleJS

5

まず、例外的な状況では、ループが無期限に続きます。例外をキャッチして無視しないでください。

第2に、ストリームが実際にエンドでない場合、bytesReadはゼロになることはありません。メソッドの名前(ReadLineAsync)がストリームの終わりまで読み込まれることを私に暗示しないので、これが当てはまると思われます。

P.S. CanReadは、特定のストリームに対して変更されることはありません。それは、ストリームが読み込み操作を行う意味で意味があるのか​​どうかではなく、を今すぐ読み取ることができるかどうかではありません。です。

+0

ああ、私のストリームは有限の時間で終了します。 –

+0

それから、最小限のレシピを投稿してください。 –

+0

*あなたのストリームは*確実に*終了していますか? IMAP接続は、通常、複数のコマンドと応答に対して保持されます。 –

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

私はそれがあなたの入力ファイルで何かになるのだろうかと思っていますか?鉱山は

one 
two 
three 

(メモ帳++でのWindows-1252でエンコード)して、私の出力がどのように

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 

ノートだった「キーを押して続行します...」mainメソッドので、予想通り最初に印刷されました非同期に呼び出されました。CanReadは、ファイルが読み取り可能であることを意味するため、常にtrueです。カーソルがEOFにあることを意味する状態ではなく、ファイルがどのようにオープンされたかの状態。

+0

上記のコメントに指定されているように、私のエンコーディングは 'Encoding.GetEncoding(1252);' –

+0

Okです。 。それでも期待通りの結果が得られました。ストリームを読み込もうとしています、それはファイルからですか?またはWebリクエストのようなもの? –

+0

IMAPサーバーから。 –

0

IMAPからストリームを取得していますが、この方法はそのストリームをテキストに変換するためのものですか?
SteamReaderをストリームの周りに構築し、それをReadToEndAsyncまたはReadToEndのどちらかと呼ぶのはなぜですか?これを非同期操作にする必要はないと思っています。ストリームが電子メールのようなものであれば、読んでいる間にUIがブロックされていることにユーザーが気づくほど大変なことはありません。
あなたのコメントの1つとして示唆されているように、これはUIアプリではありません。

私の前提が間違っている場合は、この機能がどのように使用されているかについての詳細をいくつか質問してください。あなたが私たちに伝えることができる情報が多くなればなるほど、私たちの答えはより良くなります。

EDIT: 私はあなたのメソッドがReadLineAsyncと呼ばれていることに気付きましたが、コード内で行末を探しているところはありません。あなたの意図がテキスト行を読むことであるなら、SteamReaderはReadLineとReadLineAsyncも提供します。私のPOVから

+0

実際には、このコードは速度と非同期性のためにマルチテナントアプリケーションで使用されています。ここでは、IMAPからストリームを取得しています。このメソッドは、そのストリームを 'Encoding.GetEncoding(1252); '。 –

+0

非同期は必然的に高速です。私は本当にあなたがマルチテナントアプリの意味を理解していない。これはサーバーコンポーネントの一種ですか?もしそうなら、スレッドプールはこの操作をスケーリングするためのより良い手段を提供するかもしれません。 – pipTheGeek

0

、あなたのコードは、次の操作を実行しようとしているように見えます:

  • MemoryStream(その中にすべてのそれらのチャンクを連結し、1024オクテットチャンクのシーケンスとして全体の流れを読んで
  • 指定されたエンコーディングを使用して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のアイデアはいいですが、メモリストリームへの書き込みに必要な時間は、実際のpeformするのに必要な時間との差の1つの聖霊降臨祭を行うのに十分ではない、控えめに言っても、ですI/O(おそらくあなたの入力ストリームはディスクまたはネットワークI/Oを行っています)。

+0

コードはコンパイルされません。あなたは 'return await reader.ReadToEndAsync();'を意味しましたか? – svick

+0

なぜ3つのダウンボックスが得られたのですか?単純なコンパイルエラー(これは私が修正の自由を取った)以外にこの答えに間違いはありません。 –

+0

@IsakSavoあなたが修正した方法は間違っています。このように、 'ReadToEndAsync()'が実際に完了する前に、 'reader'は' Dispose() 'dになります。コンパイルエラーはそれほど簡単ではありません。 – svick

関連する問題