You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SocketAsyncEventArgs时TaskCompletionSource死锁问题排查

控制台异步TCP服务器死锁问题修复

你的代码核心问题是创建了两个不相关的TaskCompletionSource,导致等待的任务永远无法完成:

在AcceptClient方法中,你初始化了两个TaskCompletionSource<Socket>:

  • 一个是source,你最终await source.Task等待它完成
  • 另一个是new TaskCompletionSource<Socket>(),赋值给了args.UserToken

但在OnAcceptCompleted回调里,你完成的是args.UserToken对应的那个TaskCompletionSource,而不是你在等待的source。这就导致source.Task一直处于未完成状态,await永远卡住,代码无法执行到后续的打印逻辑。

同时你对线程的推测是错误的:SocketAsyncEventArgs.Completed事件默认就是在ThreadPool线程上触发的,和死锁无关。

修复步骤

  1. 把args.UserToken直接赋值为你要等待的source,而非新建对象
  2. 在回调完成后,清理SocketAsyncEventArgs的事件订阅和资源,避免内存泄漏
  3. 完善取消逻辑,确保CancellationToken能正确终止Socket操作

修正后的完整代码

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

class Program
{
    private static CancellationTokenSource _shutdownSource = new CancellationTokenSource();

    static async Task Main()
    {
        using var listener = new Socket(SocketType.Stream, ProtocolType.Tcp);
        listener.Bind(new IPEndPoint(IPAddress.Any, 12345));
        listener.Listen(10);

        Console.WriteLine("服务器已启动,等待连接...");

        while (!_shutdownSource.IsCancellationRequested)
        {
            try
            {
                var socket = await AcceptClient(listener, _shutdownSource.Token).ConfigureAwait(false);
                Console.WriteLine($"Client accepted {socket.RemoteEndPoint}");
                // 异步处理客户端连接,避免阻塞主线程
                _ = HandleClientAsync(socket, _shutdownSource.Token);
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("服务器正在关闭...");
                break;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"接受客户端出错: {ex.Message}");
            }
        }
    }

    private static async Task<Socket> AcceptClient(Socket listener, CancellationToken token)
    {
        var source = new TaskCompletionSource<Socket>(TaskCreationOptions.RunContinuationsAsynchronously);
        using var args = new SocketAsyncEventArgs();
        
        // 将需要等待的TaskCompletionSource直接绑定到UserToken
        args.UserToken = source;
        args.Completed += OnAcceptCompleted;

        try
        {
            if (!listener.AcceptAsync(args))
            {
                // 同步完成时直接触发回调逻辑
                OnAcceptCompleted(listener, args);
            }

            using (token.Register(() => 
            {
                source.TrySetCanceled();
                // 主动取消Socket异步操作
                listener.CancelConnectAsync(args);
            }))
            {
                return await source.Task.ConfigureAwait(false);
            }
        }
        finally
        {
            // 移除事件订阅,防止内存泄漏
            args.Completed -= OnAcceptCompleted;
        }
    }

    private static void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
    {
        var source = (TaskCompletionSource<Socket>)args.UserToken;
        try
        {
            if (args.SocketError == SocketError.Success)
            {
                source.TrySetResult(args.AcceptSocket);
            }
            else if (args.SocketError == SocketError.OperationAborted)
            {
                source.TrySetCanceled();
            }
            else
            {
                source.TrySetException(new InvalidOperationException($"Socket error = {args.SocketError}"));
            }
        }
        finally
        {
            // 清理Socket引用,避免资源占用
            args.AcceptSocket = null;
        }
    }

    private static async Task HandleClientAsync(Socket socket, CancellationToken token)
    {
        using (socket)
        {
            try
            {
                var buffer = new byte[1024];
                while (!token.IsCancellationRequested)
                {
                    var received = await socket.ReceiveAsync(buffer, SocketFlags.None, token).ConfigureAwait(false);
                    if (received == 0)
                    {
                        Console.WriteLine($"客户端 {socket.RemoteEndPoint} 断开连接");
                        break;
                    }
                    Console.WriteLine($"收到来自 {socket.RemoteEndPoint} 的数据: {System.Text.Encoding.UTF8.GetString(buffer, 0, received)}");
                }
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine($"客户端 {socket.RemoteEndPoint} 连接被取消");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"处理客户端 {socket.RemoteEndPoint} 出错: {ex.Message}");
            }
        }
    }
}

关键修改说明

  • TaskCompletionSource绑定修正:确保回调完成的是主线程等待的同一个任务,解决根本的等待阻塞问题
  • 资源泄漏防护:通过using和finally块清理SocketAsyncEventArgs的事件订阅和资源
  • 取消逻辑完善:在CancellationToken触发时,主动取消Socket操作,确保回调能正确响应取消状态
  • 客户端处理优化:新增HandleClientAsync方法,演示异步处理客户端读写的标准范式,避免阻塞主线程的连接接收逻辑

内容的提问来源于stack exchange,提问作者Zergatul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 14:06:18