代码之家  ›  专栏  ›  技术社区  ›  annakata

两次处理插座/终接时出现问题?

  •  2
  • annakata  · 技术社区  · 17 年前

    我正在为一个打开套接字、发出请求并侦听响应的类编写一些代码(我急于添加的不是我的代码,我一点也不相信),这个类以一种我在xunit中测试时无法理解的方式抛出异常。我 同样的异常也会发生在“live”中,但该类是由单例引用的,因此它可能只是隐藏的。

    问题在xunit中表现为“System.CannotUnloadAppDomainException:卸载appdomain时出错”,内部异常是“System.ObjectDisposedException”,在关闭套接字时(本质上)在终结器内抛出!没有其他对socket的引用,调用close和dispose在socket类上受保护,因此我不清楚该对象还可以如何处理。

    此外,如果我仅仅捕获并吸收ObjectDisposedException,xunit将在它到达关闭侦听器线程的行时终止。

    我只是不知道在要求关闭之前如何处理该套接字。

    我对套接字的了解只是我发现这个问题后学到的,所以我不知道我是否提供了可能需要的一切。如果不是的话,请滚开!

    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;
                }
            }
        }
    }
    
    3 回复  |  直到 17 年前
        1
  •  8
  •   Jon Skeet    17 年前

    如果此对象符合垃圾收集的条件,并且没有对套接字的其他引用,则 终结器可能在对象的终结器之前运行。我想这就是这里发生的事情。

    对非托管资源的引用,这些资源几乎总是以intptr的形式存在。有序关闭应该是标准-终结器通常只在程序正在关闭或者有人忘记处理要启动的实例时运行。

    (我知道你一开始就澄清了这不是你的代码-我只是想解释一下为什么它有问题。如果你已经知道了一些/所有这些,我向你道歉。)

        2
  •  4
  •   Coincoin    17 年前

    由于垃圾收集器和终结器的工作方式,只有在类是 直接的 非托管资源的所有者,如窗口句柄、GDI对象、全局句柄或任何其他类型的IntPtr。

    终结器不得尝试处置或甚至使用托管资源,否则您将有调用终结或处置对象的风险。

    very important Microsoft article 有关垃圾收集工作原理的更多详细信息。此外,这是上的MSDN参考 Implementing Finalize and Dispose to Clean Up Unmanaged Resources ,请仔细查看底部的建议。

    简言之:

    • 终结器决不能使用任何其他资源,而只能使用正在释放和自我管理的非托管资源。如果不这样做,将有引用已收集或已处置对象的风险,因为对象在最终确定之前会被临时删除。
        3
  •  0
  •   annakata    17 年前

    新信息:看起来我实际上有两个问题,但是线程一个 appears 这是相当有毒的。

    可以捕获的异常,但它 将自动再次在 挡块的末端。”

    该链接中还包含一些非常有趣的社区内容,包括 "Thread.Abort is a Sign of a Poorly Designed Program"

    所以至少我现在有一些弹药可以换了:)