GC.Collect
似乎在后台线程中启动垃圾收集,然后立即返回。如何同步运行GC.Collect
- 即等待垃圾收集完成?
这是在NUnit测试的上下文中。我尝试将gcConcurrent设置添加到我的测试程序集的app.config文件中,我尝试使用nunit.exe.config。没有任何影响 - 当我调试时,我仍然可以看到终结器在“GC Finalizer Thread”上运行,而不是调用GC.Collect
(NUnit的“TestRunnerThread”)的线程,并且两个线程同时运行
背景:我希望我的测试在他们泄漏(不要调用Dispose on)特定类时失败。所以我在该类中添加了一个终结器,用于设置静态wasLeaked
标志;然后我的测试TearDown调用GC.Collect()
然后如果wasLeaked
为真则抛出。但它并没有确定性地失败,因为当它读取wasLeaked
时,终结器通常甚至还没有被调用。 (在垃圾收集最终完成之后,它在一些后来的测试中失败了。)
答案 0 :(得分:13)
终结器在专用的高优先级后台线程上运行。从你帖子的背景中我收集到你可以简单地做到
GC.Collect();
GC.WaitForPendingFinalizers();
Collect()
将安排任何非根实例进行最终确定,然后线程将等待终结器线程完成。
答案 1 :(得分:5)
您可以使用GC.RegisterForFullGCNotification
,使用GC.Collect(GC.MaxGeneration)
触发完整集合,然后触发GC.WaitForFullGCComplete
和GC.WaitForPendingFinalizers
方法,但请确保仅在测试中使用此方法,不应该用于生产代码。
答案 2 :(得分:2)
更简单/更好的方法可能是使用模拟并检查Dispose被明确调用的期望。
使用RhinoMocks的示例
public void SomeMethodTest()
{
var disposable = MockRepository.GenerateMock<DisposableClass>();
disposable.Expect( d => d.Dispose() );
// use constructor injection to pass in mock `DisposableClass` object
var classUnderTest = new ClassUnderTest( disposable );
classUnderTest.SomeMethod();
disposable.VerifyAllExpectations();
}
如果该方法需要创建然后处理该对象,那么我将使用并注入一个能够创建模拟对象的工厂类。以下示例使用工厂存根,因为它不是我们在此测试中测试的内容。
public void SomeMethod2Test()
{
var factory = MockRepository.Stub<DisposableFactory>();
var disposable = MockRepository.GenerateMock<DisposableClass>();
factory.Stub( f => f.CreateDisposable() ).Return( disposable );
disposable.Expect( d => d.Dispose() );
// use constructor injection to pass in mock factory
var classUnderTest = new ClassUnderTest( factory );
classUnderTest.SomeMethod();
disposable.VerifyAllExpectations();
}
答案 3 :(得分:2)
无论您是否使用并发GC,终结器始终在单独的线程上运行。如果您想确保已经运行终结器,请尝试使用GC.WaitForPendingFinalizers
。