代码之家  ›  专栏  ›  技术社区  ›  Mark Carpenter

如何模拟此应用程序挂起场景?

  •  3
  • Mark Carpenter  · 技术社区  · 15 年前

    我有一个windows窗体应用程序,它自己可以启动不同的线程来完成不同的工作。偶尔,所有线程(包括ui线程)都会冻结,我的应用程序也会变得无响应。我决定这可能是一个垃圾收集器相关的问题,因为GC将冻结所有 管理 临时线程。为了验证托管线程是否被冻结,我启动了一个非托管线程,它每秒用时间戳写入一个“heartbeat”文件,并且不受影响(即它仍在运行):

    public delegate void ThreadProc();
    
    [DllImport("UnmanagedTest.dll", EntryPoint = "MyUnmanagedFunction")]
    public static extern void MyUnmanagedFunction();
    
    [DllImport("kernel32")]
    public static extern IntPtr CreateThread(
        IntPtr lpThreadAttributes,
        uint dwStackSize,
        IntPtr lpStartAddress,
        IntPtr lpParameter,
        uint dwCreationFlags,
        out uint dwThreadId);    
    
    uint threadId;
    ThreadProc proc = new ThreadProc(MyUnmanagedFunction);
    IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(proc);
    IntPtr threadHandle = CreateThread(IntPtr.Zero, 0, functionPointer, IntPtr.Zero, 0, out threadId);
    

    我的问题是:如何模拟这种情况,即所有托管线程都挂起,但非托管线程仍在旋转?

    我的第一刀 :

    private void button1_Click(object sender, EventArgs e) {
        Thread t = new Thread(new ThreadStart(delegate {
            new Hanger();
            GC.Collect(2, GCCollectionMode.Forced);
        }));
        t.Start();
    }
    class Hanger{
        private int[] m_Integers = new int[10000000];
        public Hanger() { }
        ~Hanger() { Console.WriteLine("About to hang...");
    
        //This doesn't reproduce the desired behavior
        //while (true) ;
    
        //Neither does this
        //Thread.Sleep(System.Threading.Timeout.Infinite); 
        }
    }
    

    提前谢谢!!

    4 回复  |  直到 10 年前
        1
  •  1
  •   Thomas Pornin    15 年前

    终结器与“正常”线程执行同时执行。我们通常说gc运行终结器,但是gc检测哪些实例有应该运行的终结器,并将它们存储在一个专用队列中会更准确。一个(隐藏的)线程从队列中获取实例并运行终结器。需要这样的异步性,例如,因为终结器本身可能分配内存并可能触发gc。还有其他的 good reasons 为什么终结器必须是异步的。

    底线是你不能改变 ~Hanger() ,虚拟机在GC暂停期间执行的操作,因为将实际运行的线程 ~() 也在那时暂停。

        2
  •  0
  •   Marek    15 年前

    我知道这并不能回答您的问题,但我怀疑您的代码中存在死锁,而不是奇怪的gc问题。

    我建议你检查一下你的代码是否有死锁,特别是像 Control.Invoke 从后台线程执行UI更新时调用。确保在调用 Invoke -这可能会导致意外的死锁(好像预期会出现任何死锁:)

        3
  •  0
  •   Mark Carpenter    15 年前

    这个问题实际上是由垃圾收集器引起的。在用windbg调试和分析内存转储数天后,我们意识到存在死锁情况,但这是由gc并发收集引起的。 Changing the GC to collect non-concurrently 解决了我们的问题。

        4
  •  0
  •   Schedler    15 年前

    支持marek的答案,这看起来很像是您正在使用的并发模型的设计问题。作为一个设计问题,这是你无法通过测试有效解决的问题。

    我的建议是仔细考虑您使用的并发模型,并相应地更正设计。首先,研究死锁的必要条件,例如:

    1. 你们有哪些相互排斥的条款?
    2. 您的流程(已经在使用某些资源)需要哪些额外资源?
    3. 使用这些资源的过程需要显式释放哪些资源?

    考虑到这些因素,如果您有循环的资源分配结构,那么您将研究可能的死锁情况。