C# TcpClient异常死锁

C# TcpClient异常死锁,c#,async-await,deadlock,tcpclient,contextswitchdeadlock,C#,Async Await,Deadlock,Tcpclient,Contextswitchdeadlock,在我继承的一些代码中,我有一个奇怪的行为——下面的简化示例演示了如果内置到普通控制台应用程序中的问题 WhoIs在第5次呼叫时点击其使用限额-并返回消息+关闭插座。使用ReadLineAsync,这会产生一个SocketException和一些IOException-有时这些被捕获在catch块中,一切都是它应该的,大多数时候它们没有被捕获,程序只是挂起-VS调试器中的Break all在主线程上对Console.WriteLine的一个调用中向我显示。直接在调试器外部运行.exe文件时,此行为

在我继承的一些代码中,我有一个奇怪的行为——下面的简化示例演示了如果内置到普通控制台应用程序中的问题

WhoIs在第5次呼叫时点击其使用限额-并返回消息+关闭插座。使用ReadLineAsync,这会产生一个SocketException和一些IOException-有时这些被捕获在catch块中,一切都是它应该的,大多数时候它们没有被捕获,程序只是挂起-VS调试器中的Break all在主线程上对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();
    }
}
}
使用系统;
使用System.IO;
使用System.Net.Sockets;
使用系统文本;
使用System.Threading.Tasks;
命名空间AsyncIssueConsole应用程序
{
班级计划
{
静态void Main(字符串[]参数)
{
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();
}
专用静态异步任务LookupAsync(字符串域)
{
StringBuilder=新的StringBuilder();
TcpClient tcp=新的TcpClient();
wait tcp.ConnectAsync(“whois.pir.org”,43);
字符串strDomain=“”+域+“\r\n”;
byte[]bytDomain=Encoding.ASCII.GetBytes(strDomain.ToCharray());
尝试
{
使用(streams=tcp.GetStream())
{
wait s.WriteAsync(bytDomain,0,strDomain.Length);
使用(StreamReader sr=新的StreamReader(s,Encoding.ASCII))
{
尝试
{
//这很好
/*而(sr.Peek()>=0)
{
AppendLine(等待sr.readlinesync());
}*/
//这不是-产生SocketException,通常不会在下面捕获
字符串strLine=await sr.ReadLineAsync().ConfigureAwait(false);
while(null!=strLine)
{
建筑商附录线(斯特林);
strLine=await sr.ReadLineAsync().ConfigureAwait(false);
}
}
捕获(例外e)
{
//有时会捕获SocketException/IOException,有时则不会
返回builder.ToString();
}
}
}
}
捕获(例外e)
{
返回builder.ToString();
}
返回builder.ToString();
}
}
}
建议可能与此相关,但没有回答我可以看到的这个问题,当然不是完全:即,我需要对SynchronizationContext做什么-我已经在使用ConfigureWait(false)

当代码如上所述处于死锁状态时,堆栈跟踪为:

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#
mscorlib.dll!System.Threading.Monitor.Wait(对象obj,int毫秒计时,bool exitContext)未知
mscorlib.dll!System.Threading.Monitor.Wait(对象obj,int毫秒)未知
mscorlib.dll!System.Threading.ManualResetEventSlim.Wait(int毫秒估计值=-1,System.Threading.CancellationToken CancellationToken)未知
mscorlib.dll!System.Threading.Tasks.Task.SpinThenBlockingWait(int毫秒计时,System.Threading.CancellationToken CancellationToken)未知
mscorlib.dll!System.Threading.Tasks.Task.InternalWait(int毫秒估计值=-1,System.Threading.CancellationToken CancellationToken)未知
mscorlib.dll!System.Threading.Tasks.Task.GetResultCore(bool waitCompletionNotification=true)未知
mscorlib.dll!System.Threading.Tasks.Task.Result.get()未知
AsyncIssueConsoleApplication.exe!AsyncIssueConsoleApplication.Program.Main(字符串[]args={string[0]})第18行C#
IOException为:{“无法从传输连接读取数据:远程主机强制关闭了现有连接。”}


SocketException是:{“主机上的软件中止了已建立的连接”}

我现在可以复制它了。为了看它挂在哪里,我切换到同步IO。异步IO的一个常见调试问题是,您无法看到当前挂起的IOs。这是您可能不想首先使用异步IO的一个关键原因

它挂起,因为远程服务器未关闭连接。只有在远程端关闭连接时,
ReadLine
调用才会结束

这可能是速率限制代码中的错误。这也可能是协议的预期行为。也许你是命中注定要派