[AsyncHandle]什么引发了ObjectDisposedException?
Version |
Date |
Creator |
Description |
|
|
郑昀 |
草稿 |
继续阅读之前,我们假设您熟悉以下知识:
n 用IDisposable接口释放dotNET资源
如果不熟悉这些知识点,可以看本文最后资源列表中的文章。
本文讨论了出现不可捕获的ObjectDisposedException异常崩溃,寻找可能的原因,并试图解决。
[现象]
关键词: ObjectDisposedException。
表象: 我的dotNET C#服务偶尔会彻底崩溃,时机没有规律可循,崩溃时在Windows事件日志中有来自于“.NET Runtime 2.0 Error Reporting”的报告,描述的错误也颇让人费思量。
错误描述:
事件类型: 错误
事件来源: .NET Runtime 2.0 Error Reporting
事件种类: 无
事件 ID: 5000
描述:
EventType clr20r3, P1 juiceextractor.exe, P2
通过ObjectDisposedException对我们的提醒,可以了解到这是因为前面已经使用了一个disposed对象。在一个使用过disposed对象上调用其他方法时是会引发致命异常的,服务也不知道该在哪里捕获这个异常,似乎也不可捕获。但是,问题是,是谁引发的?
引申: 看到微软的一个对dotnet framework的servicepack公告中提到:当一个异步Web 请求在接收到响应之前如果被中止,则会引发ObjectDisposedException。
唯一和异步请求有点关系的是,本服务向一个socket服务器发送请求,并等候一段一段地循环接收服务器的大量数据反馈。这中间,使用了C#的异步方法BeginReceive来告知socket如何接收数据。
这个逻辑的代码如下:
C# code |
/// <summary> /// 调用异步方法BeginReceive来告知socket如何接收数据 /// </summary> IAsyncResult ar = _socket.BeginReceive(_recvBuffer.Buffer, 0, _recvBuffer.Buffer.Length, SocketFlags.None, _recvCallback, _socket); // 设定一个期限,10分钟内如果还没有结果返回, // 我们可以认为必须关掉当前socket连接了,不要再等了! ar.AsyncWaitHandle.WaitOne(new TimeSpan(0, 10, 0), true); ar.AsyncWaitHandle.Close(); …. public void RecvCallback(IAsyncResult result) {… |
也就是利用回调函数 “_recvCallback”,每次有数据的话,都直接调用它。但是,规定了如果10分钟内都没有完整结果返回,那么这个异步等候句柄将被关闭。
很简单的想法。
但是如果数据量大的话,socket服务器计算的过程非常漫长,导致返回数据的过程超过了10分钟,会发生什么事情呢?
[回答前面的问题]
这个时候,“ar.AsyncWaitHandle.Close();”是不是就导致了ObjectDisposedException呢?
假设如此的话。
第一,那我们需要调用AsyncWaitHandle.Close()这句话吗?
The Thread Pool and Asynchronous Methods中说道:“Note the call to ar.AsyncWaitHandle.Close()
. This prevents the WaitHandle
leaking until garbage collection. The leak wouldn't cause any problems in most cases (unlike, for instance, file handles leaking), but in situations where FireAndForget
would be called many, many times in quick succession, you could end up with a vast number of handles until the garbage collector started finalizing them. (This is also a bad thing in terms of performance - you shouldn't leave things to be finalised when it can be avoided.)”
那么,也许我们可以不调用它,来规避ObjectDisposedException异常崩溃?
第二,把ar.AsyncWaitHandle.WaitOne(new TimeSpan(0, 10, 0), true);等候的时间延长?比如说延长到40分钟。这样也可以避免ObjectDisposedException。
[改变接收模式]
第三,对于这种长时间执行的socket服务器,可以不用在一个端口上干等,可以发送请求用一个端口,接收数据反馈用另外一个socket端口号。
以前没有考虑到计算时间如此之长,所以才做成了socket异步接收模式。后来算法作了改变,有时候十分钟的等候都不够了。