Подтвердить что ты не робот

Удаляются ли объекты IDisposable, если программа неожиданно отключена?

Что произойдет, если программа неожиданно завершится (либо по исключению, либо по завершении процесса)? Существуют ли такие ситуации (или иначе), где программа будет завершена, но объекты IDisposable не будут удалены надлежащим образом?

Причина, по которой я спрашиваю, заключается в том, что я пишу код, который будет общаться с периферией, и я хочу убедиться, что нет шансов, что он останется в плохом состоянии.

4b9b3361

Ответ 1

Если причина является исключением и выбрана из блока using или блока try catch finally, она будет удалена так, как должна. Если он не улавливается блоком using, он не устанавливается автоматически (например, он не делает, когда приложение закрывается должным образом).

Образец:

IDisposable d1 = new X();

using (IDisposable d2 = new X())
{
    throw new NotImplementedException();
}

d1.Dispose();

d1 не размещается, обычно d2. Некоторые типы исключений могут препятствовать обработке блоков using, а также некоторых сбоев программы. Если причиной является сбой питания или сбоя системы, вы ничего не можете сделать, конечно.

Ответ 2

Если программа неожиданно завершает работу (например, вы убиваете процесс), нет никаких гарантий, что будет вызван метод IDisposable.Dispose. Вам лучше не полагаться на это для таких событий. Код Dispose должен вызываться вручную по вашему коду, это не то, что CLR автоматически вызовет для вас.

Ответ 3

В дополнение к Патрику Хофману и Алексею ответ на очистку может не выполняться, даже если приложение заканчивается правильно.

Как вам известно, метод Dispose не вызывается, когда сборщик мусора собирает объект, реализующий интерфейс IDisposable. Но GC вызовет метод Finalize, также известный как финализатор. В ней вы должны написать свою логику очистки, используя Dispose Pattern. И да,.NET Framework попытается запустить все финализаторы, но нет гарантии, что они когда-либо будут выполнены.

В качестве примера, программа имеет очень длинный финализатор. Поэтому .Net завершит процесс, и вы никогда не увидите сообщение.

class FinalizableObject
{
    ~FinalizableObject()
    {
        Thread.Sleep(50000);
        Console.WriteLine("Finalized");
    }
}

class Program
{
    static void Main(string[] args)
    {
        new FinalizableObject();
    }
}

Это может быть вызвано любой продолжительной операцией, например, отпустите сетевой дескриптор или что-то еще, что потребует много времени.

Поэтому вы никогда не должны полагаться на финализаторы и одноразовые объекты. Но все открытые дескрипторы объектов ядра будут автоматически закрыты, поэтому вы не должны беспокоиться о них.

Я рекомендую вам прочитать несколько интересных статей о финализаторах и GC в дополнение к ответам:

Ответ 4

Очень простой тест с использованием консольного приложения иллюстрирует, что Dispose не вызывается при обработке процесса:

class DisposableTest : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Dispose called");
    }
}

...

using (DisposableTest sw = new DisposableTest())
{
    Thread.Sleep(20000);
}

Убивание процесса с помощью диспетчера задач не вызовет метод Disposable.Dispose(). Ожидание в течение 20 секунд.

Итак, как уже упоминалось, не полагайтесь на объекты, которые можно использовать в случае сбоя приложения или его уничтожения. Однако исключения должны вызвать это. Мне просто интересно, будут ли исключения, такие как StackOverflowException или OutOfMemoryException, всегда вызывать Dispose().

[править]

Просто испытал мои раритеты:

  • StackOverflowException завершает процесс, поэтому вызов Dispose() не называется
  • OutOfMemoryException позволяет нормальный вызов Dispose()

Ответ 5

Да, есть такие ситуации. Например, вызов TerminateProcess, вызов Environment.FailFast или столкновение с внутренней ошибкой CLR приведет к завершению процесса без использования какого-либо дополнительного кода. В таких ситуациях лучшее, что вы можете сделать, это сказать "хорошо".

Даже если процесс не завершится неожиданно, вызов Dispose является ручным действием. Это не что-то сделано во время выполнения, за исключением случаев, когда объект, реализующий финализатор, который вызывает Dispose, собирает мусор. Поэтому, забывая об обертывании одноразового использования в using или вызывая утечку памяти, которая удерживает объект в живых, еще один способ Dispose никогда не может быть вызван.

Единственная надежная очистка выполняется операционной системой при выходе из процесса - все открытые ручки для системных объектов закрыты. Когда последний дескриптор закрыт, произойдет какая-либо очистка, реализованная в ОС или драйвере. Если этот код очистки не является частью драйвера, но должен быть вызван пользовательским процессом, все, что вы можете сделать, это сделать ваш код максимально надежным или реализовать сторожевой процесс, который обрабатывает очистку для вас.

Ответ 6

IDisposable - это просто интерфейс. Абсолютно ничего особенного в том, как они обрабатываются. Когда вы вызываете Dispose на IDisposable (явно или через блок использования), он вызывает содержимое вашего метода Dispose. Он получает мусор, собранный, как любой другой объект.

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

Если все эти ресурсы управляются, сбор мусора может быть достаточным, и реализации могут быть просто оптимизированы.

Если они неуправляемы или имеют некоторое соединение с неуправляемыми ресурсами, сбор мусора, вероятно, недостаточно. Вот почему полная рекомендуемая реализация IDisposable включает в себя обработку как явной утилизации, так и утилизации во время выполнения (через финализатор).

Отключения процесса не будут вызываться Dispose, и финализаторы не гарантированно будут запускаться... поэтому вам нужно надеяться, что само уничтожение процесса будет достаточно.