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

如何在C# Socket场景中正确避免ObjectDisposedException?

如何避免Socket异步回调中的ObjectDisposedException

首先咱们先搞懂为什么会出现这个错误:当你调用Dispose()关闭_serverSocket之后,之前通过BeginAccept发起的异步操作的回调OnAcceptConnection可能还在排队执行,这时候在回调里调用EndAccept,就会因为Socket已经被释放而抛出ObjectDisposedException。你原来写的if (_serverSocket == null)检查完全没用——因为Dispose()里只是关闭了Socket,并没有把_serverSocket变量置为null,它还是指向那个已经被释放的对象实例。

接下来咱们一步步按照最佳实践修正这个问题,都是适合自学C#的新手理解的方案:

1. 添加线程安全的已释放状态标志

异步回调和Dispose()可能在不同线程执行,所以需要一个能跨线程同步的状态标记。添加一个volatile修饰的布尔字段(volatile保证多线程下变量的可见性,避免线程读到过期的状态):

internal sealed class SocketHandler : IDisposable { 
    private static readonly ILogger Logger = LogManager.GetCurrentClassLogger(); 
    private readonly Socket _serverSocket; 
    private volatile bool _isDisposed; // 新增:标记对象是否已释放
    // ... 其他原有代码
}

2. 完善Dispose方法,保证状态同步

在Dispose()里先标记_isDisposed为true,再关闭Socket,同时加锁避免和回调操作并发冲突:

public void Dispose() { 
    if (_isDisposed) return; // 避免重复调用Dispose
    lock (_serverSocket) { // 加锁防止和回调线程的并发操作
        _isDisposed = true;
        _serverSocket.Close(); 
    }
}

3. 在回调中优先检查已释放状态

在OnAcceptConnection的最开始就检查_isDisposed,如果对象已经释放,直接终止流程,不要执行后续操作:

private void OnAcceptConnection(IAsyncResult asyncResult) { 
    // 先检查是否已释放,直接终止无效流程
    if (_isDisposed) {
        return;
    }
    try { 
        // 原有业务代码...
        var client = ((Socket) asyncResult.AsyncState).EndAccept(asyncResult); 
        // ... 后续连接判断、玩家添加逻辑
    } catch (SocketException socketException) { 
        Logger.Fatal(socketException, "Failed to accept socket connection."); 
    } catch (ObjectDisposedException ex) {
        // 兜底捕获:多线程环境下可能出现检查状态后,对象刚好被释放的竞态条件
        Logger.Warn(ex, "Attempted to accept connection on a disposed socket.");
    } finally { 
        // 只有当对象未释放时,才继续监听新连接
        if (!_isDisposed) {
            _serverSocket.BeginAccept(OnAcceptConnection, _serverSocket); 
        }
    } 
}

为什么这是最佳实践?

  • 提前终止无效流程:通过状态标志快速过滤掉已释放后的回调,避免不必要的操作
  • 线程安全的资源释放:用锁保证标记状态和关闭Socket的原子性,防止并发冲突
  • 兜底异常捕获:多线程环境下可能出现“检查状态后,对象刚好被释放”的竞态条件,捕获ObjectDisposedException作为最后一道防线
  • 停止后续异步操作:在finally块里只在未释放时重新发起BeginAccept,避免Dispose之后还继续监听新连接

给自学C#的你的小建议

  • 实现IDisposable的类,记得遵循Dispose模式(如果类包含非托管资源需要更复杂的实现,这里Socket是托管资源,当前简化版足够)
  • 异步操作的回调一定要考虑对象的生命周期,尤其是类被释放时的边界情况
  • 多线程环境下的变量访问,要注意可见性和原子性,volatile和lock是新手最容易掌握的解决手段

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:11:14