我正在使用一些代码(不是我的,我不得不添加,我完全不相信这一点)对于一个打开套接字,发出请求和侦听响应的类,这会引发异常在xunit中测试时我无法理解的方式。我假设相同的异常发生在“实时”但是该类由单例引用,因此它可能只是隐藏。
问题在xunit中显示为“System.CannotUnloadAppDomainException:在卸载appdomain时出错”,并且在关闭套接字时,内部异常是“System.ObjectDisposedException”抛出(基本上)在终结器内!在Socket类中没有其他对socket调用close和dispose受保护的引用,所以我不清楚该对象是如何处理的。
此外,如果我只是捕获并吸收ObjectDisposedException,则xunit在命中该行以关闭侦听器线程时终止。
我只是不知道Socket在被要求关闭之前是如何处置的。
我对套接字的了解只是我在发现这个问题后所学到的,所以我不知道我是否提供了所需的一切。 LMK,如果没有!
public class Foo
{
private Socket sock = null;
private Thread tListenerThread = null
private bool bInitialised;
private Object InitLock = null;
private Object DeInitLock = null;
public Foo()
{
bInitialised = false;
InitLock = new Object();
DeInitLock = new Object();
}
public bool initialise()
{
if (null == InitLock)
return false;
lock (InitLock)
{
if (bInitialised)
return false;
sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 8);
sock.Bind( /*localIpEndPoint*/);
sock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(mcIP));
tListenerThread = new Thread(new ThreadStart(listener));
tListenerThread.Start();
bInitialised = true;
return true;
}
}
~Foo()
{
if (bInitialised)
deInitialise();
}
private void deInitialise()
{
if (null == DeInitLock)
return;
lock (DeInitLock)
{
if (bInitialised)
{
sock.Shutdown(SocketShutdown.Both); //throws System.ObjectDisposedException
sock.Close();
tListenerThread.Abort(); //terminates xunit test!
tListenerThread = null;
sock = null;
bInitialised = false;
}
}
}
}
答案 0 :(得分:8)
如果此对象符合垃圾回收条件且没有其他对Socket的引用,那么套接字终结器可能会在对象的终结器之前运行。我怀疑这就是这里发生的事情。
在终结器中做这么多工作通常是一个坏主意(IMO)。我记不起上次实现终结器了 - 如果你实现了IDisposable,你应该没问题,除非你有直接引用非托管资源,这几乎总是以IntPtrs的形式。有序关闭应该是常态 - 如果程序正在关闭,或者有人忘记处理实例,那么终结器通常只能运行。
(我知道你在开始时澄清说这不是你的代码 - 我只是想我会解释为什么会有问题。如果你已经知道了部分/全部内容,请道歉。)
答案 1 :(得分:4)
由于垃圾收集器和终结器的工作方式,只有当您的类是非托管资源的直接所有者(例如Window Handle,GDI对象,全局句柄)时,才能使用终结器。或任何其他类型的IntPtr。
终结者不得试图处置甚至使用托管资源,否则您将冒险调用已敲定或已弃置的对象。
我强烈建议您阅读此very important Microsoft article以获取有关垃圾收集工作原理的更多详细信息。此外,这是Implementing Finalize and Dispose to Clean Up Unmanaged Resources上的MSDN参考,请仔细查看底部的建议。
简而言之:
答案 2 :(得分:0)
新信息:看起来我实际上遇到了两个问题,但是线程appears非常有毒。
从上面的MSDN链接:
“ThreadAbortException是一个特殊的 可以捕获的异常,但它 将自动再次提升 捕获块结束。“
一些非常有趣的社区内容也包括"Thread.Abort is a Sign of a Poorly Designed Program"。
所以至少我现在有一些弹药来改变这种情况:)