为什么TcpClient.Connect即使同步运行,也不会在异步方法中抛出异常

本文关键字:异步方法 抛出异常 Connect TcpClient 运行 同步 为什么 | 更新日期: 2023-09-27 18:01:36

  1. 为什么即使没有指定等待,此代码也不会引发System.Net.Sockets.SocketException?(没有服务器在指定端口侦听(
  2. Why Thread.Sleep(4000(;不执行?

    public class AsyncTcpClientDemos
    {
       private static readonly ILog Logger = LogProvider.GetCurrentClassLogger();
       public async Task ConnectTcpClientNoException()
       {
           Logger.Debug("ConnectTcpClientNoException() - start");
           var tcp = new TcpClient();
           tcp.Connect("127.0.0.1", 9045);
           Thread.Sleep(4000);
       }
    }
    

方法是从NUnit测试中调用的:

[Test]
public void AsyncTcpClientNoExceptionTest()
{
    var asyncAwait = new AsyncTcpClientDemos();
    // TODO: find out why this is not throwing exception
    asyncAwait.ConnectTcpClientNoException();            
}

为什么TcpClient.Connect即使同步运行,也不会在异步方法中抛出异常

async方法从不直接抛出异常。相反,如果在方法体中引发异常,则异步方法返回的任务将出现故障。

睡眠没有被执行,因为异常确实阻止了方法的其余部分的执行——只是异常通过Task传播,而不是直接向上传播到堆栈。

如果您真的希望方法的第一部分抛出异常,可以将其一分为二。例如:

public Task FooAsync(int x, int y)
{
    if (x < y)
    {
        // This will be thrown straight to the caller, in the
        // normal way
        throw new ArgumentException("...");
    }
    return FooAsyncImpl(x, y);
}
private async Task FooAsyncImpl(int x, int y)
{
    // Any exceptions thrown here will be propagated via the task
}

为什么此代码甚至没有抛出System.Net.Sockets.SocketException如果没有指定等待?

正因为如此。您没有在等待返回的任务。如果你在等待,你会看到异常传播:

[Test]
public async Task AsyncTcpClientNoExceptionTest()
{
    var asyncAwait = new AsyncTcpClientDemos();
    await asyncAwait.ConnectTcpClientNoException();            
}

即使通过同步阻止,你也可以看到(正如@Luan所建议的((只是为了表明这一点,实际上不要使用这个(:

[Test]
public void AsyncTcpClientNoExceptionTest()
{
    var asyncAwait = new AsyncTcpClientDemos();
    asyncAwait.ConnectTcpClientNoException().GetAwaiter().GetResult();
}

当您使用async void时,您将强制执行"fire-and-forget"风格,即完全放弃出现故障的任务。这恰好产生了吞咽异常的不希望的行为。如果您注册TaskScheduler.UnobservedTaskException,您将能够看到当任务完成时,会触发此事件。

Why Thread.Sleep(4000(;不执行?

正如John所说,因为异常确实发生了,所以它根本没有反映在您编写的代码中。将任务的执行更改为正确等待将显示这一点。