同步运行 GC.Collect

发布于 2024-07-16 22:45:20 字数 663 浏览 5 评论 0原文

GC.Collect 似乎在后台线程中启动垃圾收集,然后立即返回。 如何同步运行GC.Collect——即等待垃圾收集完成?

这是在 NUnit 测试的背景下。 我尝试将 gcConcurrent 设置添加到我的测试程序集的 app.config 文件中,我对 nunit.exe.config 进行了相同的尝试。 两者都没有任何效果——当我调试时,我仍然可以看到终结器正在“GC Finalizer Thread”上运行,而不是在调用 GC.Collect 的线程(NUnit 的“TestRunnerThread”)上运行,并且两个线程同时运行。

背景:如果我的测试泄漏(不调用 Dispose)特定的类,我希望我的测试失败。 因此,我向该类添加了一个终结器,用于设置静态 wasLeaked 标志; 然后我的测试 TearDown 调用 GC.Collect() ,然后如果 wasLeaked 为 true 则抛出异常。 但它并不是确定性失败,因为当它读取 wasLeaked 时,终结器通常还没有被调用。 (在垃圾收集最终完成之后,它反而失败了一些后续测试。)

GC.Collect appears to start the garbage collection in a background thread, and then return immediately. How can I run GC.Collect synchronously -- i.e., wait for the garbage collection to complete?

This is in the context of NUnit tests. I tried adding the gcConcurrent setting to my test assembly's app.config file, and I tried the same with nunit.exe.config. Neither had any effect -- when I debug, I can still see the finalizer being run on the "GC Finalizer Thread", rather than the thread that called GC.Collect (NUnit's "TestRunnerThread"), and both threads are running concurrently.

Background: I want my tests to fail if they leak (don't call Dispose on) a particular class. So I've added a finalizer to that class that sets a static wasLeaked flag; then my test TearDown calls GC.Collect() and then throws if wasLeaked is true. But it's not failing deterministically, because when it reads wasLeaked, the finalizer usually hasn't even been called yet. (It fails some later test instead, after the garbage collection finally finishes.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

动次打次papapa 2024-07-23 22:45:20

终结器在专用的高优先级后台线程上运行。 从您帖子的背景来看,我认为您可以简单地执行

GC.Collect();
GC.WaitForPendingFinalizers();

Collect() 将安排任何非根实例进行终结,然后线程将等待终结器线程完成。

Finalizers are run on a dedicated, high-priority background thread. From the background in your post, I gather that you can simply do

GC.Collect();
GC.WaitForPendingFinalizers();

The Collect() will schedule any non-rooted instances for finalization and then the thread will wait for the finalizer thread to complete.

痕至 2024-07-23 22:45:20

您可以使用GC.RegisterForFullGCNotification,使用GC.Collect(GC.MaxGeneration)触发完整收集,然后使用GC.WaitForFullGCCompleteGC.WaitForPendingFinalizers 方法,但请确保仅在测试中使用此方法,不应将它们用于生产代码。

You can use GC.RegisterForFullGCNotification, trigger a full collection with GC.Collect(GC.MaxGeneration) and then the GC.WaitForFullGCComplete and GC.WaitForPendingFinalizers methods, but make sure to use this in your tests only, they should not be used for production code.

我只土不豪 2024-07-23 22:45:20

执行此操作的一种更简单/更好的方法可能是使用模拟并检查显式调用 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();
}

A easier/better way of doing this may be to use mocking and check an expectation that Dispose was called explicitly.

Example using 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();
}

If the method needs to create and then dispose of the object, then I would use and inject a factory class that is able to create the mock object. Example below uses stub on factory as it's not what we are testing for in this test.

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();
}
辞慾 2024-07-23 22:45:20

无论您是否使用并发 GC,终结器始终在单独的线程上运行。 如果您想确保终结器已运行,请尝试使用 GC.WaitForPendingFinalizers

Finalizers always run on a separate thread regardless of whether you're using a concurrent GC or not. If you want to ensure that finalizers have been run, try GC.WaitForPendingFinalizers instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文