Есть ли время, чтобы игнорировать IDisposable.Dispose? - PullRequest
6 голосов
/ 27 апреля 2010

Конечно, мы должны вызывать Dispose () для объектов IDisposable, как только они нам не нужны (что часто является просто областью применения «using»). Если мы не примем эту меру предосторожности, то могут произойти плохие вещи, от тонких до выставочных.

Но как насчет "последнего момента" перед завершением процесса? Если ваши IDisposables не были явно удалены к тому моменту времени, не правда ли, что это больше не имеет значения? Я спрашиваю, потому что неуправляемые ресурсы под CLR представлены объектами ядра - и завершение процесса win32 все равно освободит все неуправляемые ресурсы / объекты ядра. Иными словами, после завершения процесса никакие ресурсы не останутся «утечками» (независимо от того, был ли вызван метод Dispose () для устаревших IDisposables).

Кто-нибудь может вспомнить случай, когда завершение процесса все равно оставит утечку ресурса, просто потому, что Dispose () не был явно вызван для одного или нескольких IDisposables?

Пожалуйста, не поймите неправильно этот вопрос: я не пытаюсь оправдать игнорирование IDisposables. Вопрос чисто технико-теоретический.

РЕДАКТИРОВАТЬ: А как насчет моно работает на Linux? Является ли прекращение процесса таким же «надежным» при устранении неуправляемых «утечек»?

ПОЗДНЕЕ РЕДАКТИРОВАНИЕ: хотя для IDisposables могут существовать «иные виды использования», я сосредоточен исключительно на утечках ресурсов. Я слышал два ответа: (1) если ваш процесс откажется завершить, у вас будет утечка и (2) да, ресурсы могут утечь, даже если процесс завершится. Я, конечно, согласен с пунктом (1), хотя он выходит за рамки того, что я преследую. В противном случае пункт (2) - это именно то, что я ищу, но я не могу избавиться от ощущения, что это всего лишь предположение. Джеффри Рихтер («Windows через C / C ++») объясняет, что (изящно) завершенный процесс Win32 не оставит утечек или потерянных ресурсов. Почему процесс, содержащий CLR, изменит это? Где находится документация, конкретный пример или теоретический сценарий, который дает основание полагать, что при очистке CLR возможности очистки процесса скомпрометированы при использовании CLR?

Ответы [ 5 ]

2 голосов
/ 27 апреля 2010

Во время совместного выключения AppDomain выгружается, что приводит к выполнению всех финализаторов:

С Object.Finalize Документация:

Во время закрытия домена приложения Finalize автоматически вызывается для объектов, которые не освобождаются от завершения, даже тех, которые все еще доступны.

Таким образом, вы можете быть отключены при условии соблюдения двух критериев:

  • Каждый IDisposable объект, который все еще жив, имеет правильно реализованный финализатор (верно для классов Framework, может не быть верным для менее надежных библиотек); и

  • Это на самом деле совместное отключение, а не ненормальное отключение, такое как завершение жесткого процесса, Ctrl-C в консольном приложении или Environment.FailFast.

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

2 голосов
/ 27 апреля 2010

Технически все зависит от того, что делает IDisposable. Он использовался для многих целей, а не только для неуправляемых ресурсов.

Например, когда я работал над приложением Outlook, я создал симпатичную небольшую абстракцию Outlook API. Вложения особенно раздражали в работе с потоками, потому что вам нужно было сохранить их во временном файле, поработать с ними, а затем очистить.

Итак, моя абстракция выглядела примерно так:

OutlookMailItem mailItem = blah;
using (Stream attachmentStream = mailItem.OpenAttachment("something.txt")) {
   // work with stream
}

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

В действительности, почти все реализации IDisposable, которые заключают в себе какой-либо сокет, дескриптор или транзакцию, будут просто очищены операционной системой после завершения процесса. Но, очевидно, это как благосостояние. Избегайте этого, если можете.

1 голос
/ 27 апреля 2010

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

И да, моя программа оказалась вРаньше состояние зомби, а затем клиент жалуется, что программа больше не работает, потому что программа не может подключиться к последовательному порту при перезапуске.В результате пользователь либо удаляет процесс в диспетчере задач, либо перезагружает его, что не особенно удобно для пользователя.

0 голосов
/ 27 апреля 2010

Во-первых, я хотел бы отметить, что IDisposable не только для объектов ядра Windows или неуправляемой памяти. Скорее, это для вещей, которые сборщик мусора не понимает (из которых объекты ядра являются особым случаем).

Небольшой пример, просто чтобы дать вам идею:

    sealed class Logger : IDisposable
    {
        private bool _disposed = false;

        public Logger()
        {
            System.IO.File.AppendAllText( @"C:\mylog.txt", "LOG STARTED" );
        }

        ~Logger()
        {
            Dispose();
        }

        public void Dispose()
        {
            if ( !_disposed )
            {
                System.IO.File.AppendAllText( @"C:\mylog.txt", "LOG STOPPED" );
                _disposed = true;
            }
        }

        public void WriteMessage( string msg )
        {
            System.IO.File.AppendAllText( @"C:\mylog.txt", "MESSAGE: " + msg );
        }
    }

Обратите внимание, что этот класс Logger не "удерживает" никакие ресурсы в стиле объекта ядра. Он открывает файл и сразу же закрывает его. Однако за этой логикой стоит написать «LOG STOPPED», когда объект уничтожен. Эта логика - это то, что GC не может понять - и это место для использования IDisposable.

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

Тем не менее, при условии, что ваши одноразовые объекты правильно написаны (то есть вызывается Dispose в финализаторе), они все равно будут удалены CLR при выходе из процесса.

Итак, ответ: да, нет необходимости звонить Dispose непосредственно перед выходом из проекта. Но НЕ потому, что одноразовые ресурсы являются объектами ядра (потому что они не обязательно есть).


Редактировать

Как правильно заметил Джош Эйнштейн, финализаторы на самом деле не гарантированно работают при выходе из процесса. И тогда ответ становится: всегда вызывайте Dispose, просто чтобы подать в суд

0 голосов
/ 27 апреля 2010

Вы пишете свой код для освобождения объектов в вашем доступном классе. Вам нужно написать код для освобождения неуправляемого и управляемого кода.

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

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

...