2016-04-28 17 views
3

私は継承したいくつかのコードで好奇心を持っています。以下の単純な例は、プレーンなコンソールアプリケーションに組み込まれている場合の問題を示しています。TcpClient例外デッドロック

WhoIsは5回目のコールで使用許可を得て、メッセージを返して+ソケットをシャットダウンします。 ReadLineAsyncを使用すると、SocketExceptionとIOExceptionが生成されます。これらはキャッチブロック内にキャッチされ、すべてがそうである必要があります。ほとんどの場合、キャッチされず、プログラムが単にハングします。メインスレッドのConsole.WriteLineを呼び出します。この現象は、デバッガの外で.exeファイルを直接実行すると引き続き発生します。

何が起こっているのか誰に見えますか?

Peek()を使って私の問題を実際に解決できますが、私は例外が捕捉されずに何が起こっているのか知りたいのですが、 "デッドロック"です。おそらく、それは何らかのスレッドや文脈の問題です。それが私がやっていることなら、どこでそれを避けることができるのかを知りたいです!

using System; 
using System.IO; 
using System.Net.Sockets; 
using System.Text; 
using System.Threading.Tasks; 

namespace AsyncIssueConsoleApplication 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.WriteLine(Task.Run(() => LookupAsync("elasticsearch.org")).Result); 
     Console.ReadLine(); 
    } 

    private static async Task<string> LookupAsync(string domain) 
    { 
     StringBuilder builder = new StringBuilder(); 
     TcpClient tcp = new TcpClient(); 
     await tcp.ConnectAsync("whois.pir.org", 43).ConfigureAwait(false); 
     string strDomain = "" + domain + "\r\n"; 
     byte[] bytDomain = Encoding.ASCII.GetBytes(strDomain.ToCharArray()); 
     try 
     { 
      using (Stream s = tcp.GetStream()) 
      { 
       await s.WriteAsync(bytDomain, 0, strDomain.Length).ConfigureAwait(false); 
       using (StreamReader sr = new StreamReader(s, Encoding.ASCII)) 
       { 
        try 
        { 
         //This is fine 
         /*while (sr.Peek() >= 0) 
         { 
          builder.AppendLine(await sr.ReadLineAsync()); 
         }*/ 

         //This isn't - produces SocketException which usually isn't caught below 
         string strLine = await sr.ReadLineAsync().ConfigureAwait(false); 
         while (null != strLine) 
         { 
          builder.AppendLine(strLine); 
          strLine = await sr.ReadLineAsync().ConfigureAwait(false); 
         } 
        } 
        catch (Exception e) 
        { 
         //Sometimes the SocketException/IOException is caught, sometimes not 
         return builder.ToString(); 
        } 
       } 
      } 

     } 
     catch (Exception e) 
     { 
      return builder.ToString(); 
     } 
     return builder.ToString(); 
    } 
} 
} 

duplicate Q&Aは確かではない、完全に、関係するかもしれませんが、私が見ることができるこのクエリに応答しません推奨:つまり私はのSynchronizationContextについて行う必要があるだろう - 私はすでに(偽)ConfigureAwaitを使用しています。上記のようにコードがデッドロックされ

、スタックトレースがある:

mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout, bool exitContext) Unknown 
mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout) Unknown 
mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int millisecondsTimeout = -1, System.Threading.CancellationToken cancellationToken) Unknown 
mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) Unknown 
mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int millisecondsTimeout = -1, System.Threading.CancellationToken cancellationToken) Unknown 
mscorlib.dll!System.Threading.Tasks.Task<string>.GetResultCore(bool waitCompletionNotification = true) Unknown 
mscorlib.dll!System.Threading.Tasks.Task<System.__Canon>.Result.get() Unknown 
AsyncIssueConsoleApplication.exe!AsyncIssueConsoleApplication.Program.Main(string[] args = {string[0]}) Line 18 C# 

にIOExceptionがある:{ "転送接続からデータを読み取ることができません:既存の接続はリモートホストに強制的に切断されました。「}

のSocketExceptionがある:{」確立された接続は、ホストマシン "}

+1

あなたのTCPClientインスタンスを破棄することは役に立ちますか? –

+0

私は 'ConnectAsync'をtryブロックに移動します – Eser

+0

あなたはTCPClientを廃棄していません - (TcpClient tcp = new TcpClient())を使って{...} –

答えて

1

私は今、それを再現することができ、ソフトウェアによって中止されました。それがぶら下がっている場所を見るために、私は同期IOに切り替えました。これはasync IOの一般的なデバッグの問題で、現在どのIOが保留中であるのかわかりません。これは、最初にasync IOを使用したくない主な理由です。

enter image description here

enter image description here

リモートサーバーが接続を閉じていないので、それはぶら下がっています。 ReadLineコールは、リモート側が接続を終了した場合にのみ終了します。

これはレート制限コードのバグである可能性があります。それはまた、プロトコルの期待される挙動であり得る。多分あなたは今すぐ次のリクエストを送るつもりですか?または、前の行からレート制限を検出し、自分自身をシャットダウンするはずです。

これはスレッドの問題ではありません。並行処理はまったくありません。すべてのLookupAsyncインスタンスが順番に実行されています。

リモートサーバが複数の接続で異なった動作をする場合は、TcpClientを正しく閉じてみました。影響はありませんでした。あなたはどんな場合でもあなたのリソースを処分する必要があります。これは深刻なリークです。

関連する問題