我有一个Windows窗体应用程序本身会启动不同的线程来执行不同类型的工作。有时,所有线程(包括UI线程)都会被冻结,我的应用程序变得无法响应。我已经确定它可能是垃圾收集器相关的问题,因为GC会暂时冻结所有托管线程。为了验证只是托管线程被冻结,我启动了一个非托管的线程,每秒写入一个带有时间戳的“心跳”文件,并且它不受影响(即它仍然运行):
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);
}
}
提前致谢!!
答案 0 :(得分:1)
终结器与“正常”线程执行同时执行。我们通常说GC运行终结器,但GC更准确地检测哪些实例具有应该运行的终结器,并将它们存储在专用队列中。 (隐藏)线程从队列中获取实例并运行终结器。需要这种异步,例如,因为终结器本身可能会分配内存并可能触发GC。还有其他good reasons为什么终结器必然是异步的。
一句话是,您无法从~Hanger()
更改VM在GC暂停期间执行的操作,因为实际运行~Hanger()
的线程此时也会暂停。
答案 1 :(得分:0)
我意识到这不能回答你的问题,但我怀疑代码中存在死锁,而不是奇怪的GC问题。
我建议在从后台线程进行UI更新时检查代码是否存在死锁,尤其是Control.Invoke
调用等间接情况。在调用Invoke
时确保没有锁定 - 这可能导致意外的死锁(好像预计会出现任何死锁)。)
答案 2 :(得分:0)
问题DID实际上源于垃圾收集器。经过多天使用WinDbg调试和分析内存转储,我们意识到存在死锁情况,但同时由GC收集引起。 Changing the GC to collect non-concurrently解决了我们的问题。
答案 3 :(得分:0)
支持Marek的答案,这看起来很像你正在使用的并发模型的设计问题。作为一个设计问题,这是你无法通过测试有效解决的问题。
我的建议是仔细考虑您使用的并发模型,并相应地更正设计。首先查看死锁的必要条件,例如:
考虑到这些因素,如果您有循环资源分配结构,那么您正在研究可能出现的死锁情况。