Запустить GC.Collect синхронно - PullRequest
14 голосов
/ 14 апреля 2009

GC.Collect появляется, чтобы запустить сборку мусора в фоновом потоке, а затем немедленно вернуться. Как я могу запустить GC.Collect синхронно, то есть дождаться завершения сборки мусора?

Это в контексте тестов NUnit. Я попытался добавить параметр gcConcurrent в файл app.config моей тестовой сборки, и я попытался сделать то же самое с nunit.exe.config. Ни один из них не имел никакого эффекта - когда я отлаживаю, я все еще вижу, как финализатор запускается в «потоке финализатора GC», а не в потоке с именем GC.Collect («TestRunnerThread» NUnit), и оба потока работают одновременно.

Справочная информация. Я хочу, чтобы мои тесты не выполнялись, если они пропускают (не вызывая Dispose) определенный класс. Итак, я добавил финализатор к этому классу, который устанавливает статический флаг wasLeaked; тогда мой тест TearDown вызывает GC.Collect(), а затем выдает, если wasLeaked - true. Но это не является детерминированным сбоем, потому что когда он читает wasLeaked, финализатор обычно даже еще не вызывается. (Вместо этого после некоторого более позднего теста он завершает сборку мусора.)

Ответы [ 4 ]

13 голосов
/ 14 апреля 2009

Финализаторы запускаются в выделенном высокоприоритетном фоновом потоке. Исходя из фона в вашем посте, я понимаю, что вы можете просто сделать

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

Collect() будет планировать любые некорневые экземпляры для завершения, а затем поток будет ждать завершения потока завершителя.

5 голосов
/ 14 апреля 2009

Вы можете использовать GC.RegisterForFullGCNotification, запускать полную коллекцию с помощью GC.Collect(GC.MaxGeneration) и затем методов GC.WaitForFullGCComplete и GC.WaitForPendingFinalizers, но убедитесь, что используете это только в своих тестах, они не должны использоваться для производственного кода.

2 голосов
/ 14 апреля 2009

Финализаторы всегда запускаются в отдельном потоке независимо от того, используете ли вы одновременный сборщик мусора или нет. Если вы хотите убедиться, что финализаторы были запущены, попробуйте вместо этого GC.WaitForPendingFinalizers.

2 голосов
/ 14 апреля 2009

Более простой / лучший способ сделать это может состоять в том, чтобы использовать насмешку и проверить ожидание того, что 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...