.NET — Финализаторы и выход(0)

У меня есть приложение .NET С#/С++, которое использует вызов exit(0) (из <stdlib.h>) в потоке для завершения.

Странно то, что при некоторых обстоятельствах финализаторы управляемых объектов вызываются сразу после вызова exit, а при других обстоятельствах они вообще не вызываются.

Обстоятельства довольно детерминированы — приложение вызывает некоторые методы из внешней dll-плагина (написанной на неуправляемом C) во время своего существования.
Если я использую dll A, всегда вызываются финализаторы.
Если я использую dll B, финализаторы никогда не вызываются.

Каково ожидаемое поведение финализаторов в случае вызова exit(0)? (если есть какое-либо ожидаемое и задокументированное поведение)

Могут ли вызовы внешних DLL изменить некоторые глобальные настройки, которые могут повлиять на способ завершения процесса?


person Cristian Diaconescu    schedule 03.04.2009    source источник
comment
Вероятно, вам следует использовать шаблон IDisposable, а не финализаторы.   -  person Joel Coehoorn    schedule 03.04.2009
comment
Я тоже об этом подумал, когда впервые увидел код, но зло уже там, в кодовой базе...   -  person Cristian Diaconescu    schedule 03.04.2009


Ответы (4)


Крис Брамм рассказал о том, как обрабатываются финализаторы во время остановки процесса:

Суть в том, что, похоже, очень мало гарантий для финализаторов, работающих при завершении работы, но я не уверен, что могут делать библиотеки DLL, чтобы заставить вещи действовать по-другому (может быть, это делает одна DLL). что-то в обработке DLL_PROCESS_DETACH, что дает .NET возможность обрабатывать финализаторы.

Статья предназначена для .NET 1.x — я не уверен, насколько это изменилось в .NET 2.0 или более поздних версиях.

person Michael Burr    schedule 03.04.2009

Согласно книге Джеффа Рихтера, система пытается вызвать финализаторы при завершении процесса, но существует как тайм-аут для каждого финализатора (2 с), так и общий тайм-аут (40 с), охватывающий этот процесс, после чего процесс прерывается. (Конечно, точные времена могли измениться к настоящему времени, это было правильно для 2.0)

Может быть, вы видите финализатор, который запускается более 2 секунд? Это приведет к прекращению финализации.

person Will Dean    schedule 03.04.2009
comment
У меня также есть книга (3-е издание), на какой странице это написано? - person Royi Namir; 06.04.2012

В конечном итоге это проблема «гонки вооружений». Кто-то регистрирует ошибку в Microsoft, жалуясь на то, что их финализаторы не работают, когда какой-то неприятный код заставляет процесс завершиться - так что проблема исправлена. Затем кто-то еще регистрирует ошибку о том, что, похоже, нет способа принудительно завершить процесс мгновенно, чтобы финализаторы не запускались, поэтому Microsoft добавляет новый API, чтобы разрешить это снова. Итак, другой человек требует новый тип «критического» финализатора, который всегда запускается даже в ответ на этот новый вид выхода... и так далее.

Так что, вероятно, было бы проще изменить код C++, чем пытаться полагаться на то, кто в настоящее время побеждает в гонке вооружений.

person Daniel Earwicker    schedule 03.04.2009
comment
TerminateProcess(GetCurrentProcess()) гарантированно не запускает финализаторы или перематывает стек (это нативные методы - P/Invoke их обоих). - person Joshua; 04.12.2009

Если код, который вы помещаете в финализаторы, довольно важен, переместите его в Dispose и поместите вызов Dispose в ваши финализаторы с шаблоном if( !disposed ) в Dispose.

В моей книге не следует полагаться на то, что финализаторы когда-либо будут вызываться во время выполнения. Dispose является явным, и у вас есть гораздо более тонкий и детерминированный контроль над ним.

person Tanveer Badar    schedule 11.04.2009