Каков рекомендуемый способ защиты от утечек ресурсов в контексте ThreadAbortException? - PullRequest
4 голосов
/ 06 июня 2011

Я работаю над улучшением безопасности кода для исключения и понял, что повышенное значение ThreadAbortException может привести к нежелательным утечкам ресурсов, даже при защите ресурсов с помощью конструкции C # using. Например, рассмотрим следующий код (который может выполняться в отдельном потоке).

using (TextWriter writer = CreateWriter(filename))
{
    // do something with the writer.
}

TextWriter CreateWriter(string filename)
{
    return new CustomWriter(File.OpenWrite(filename));
}

Если поток, выполняющий этот код, ненормально завершен, то я бы хотел, чтобы дескриптор файла, на который ссылается filename, был немедленно закрыт. Могу ли я сделать это, не заменяя использование конструкции using блоком try / finally?

Я предполагаю, что ThreadAbortException может быть поднят в любое время, что означает, что я должен обращать внимание на то, что происходит между утверждениями. Хотя я могу защититься от исключения в CreateWriter с помощью блока try / finally, конструкция using не будет делать то же самое до тех пор, пока не будет вычислено выражение в скобках, что означает, что файловый ресурс остается открытым в случае возникновения исключения сразу после CreateWriter возвращается.

Я понимаю, что финализатор в конечном итоге освободит дескриптор файла, но мне интересно, есть ли детерминистский способ решения этой проблемы без перехвата ThreadAbortException в каждом месте, где используется CreateWriter.

Ответы [ 4 ]

4 голосов
/ 06 июня 2011

Да, детерминированный способ предотвратить это - не использовать Thread.Abort. Когда-либо. Подайте сигнал темам, что пора останавливаться, и пусть они завершаются изящно. Thread.Abort - это большая большая красная сельдь, помещенная в API исключительно для того, чтобы сбить вас с толку. ;)

http://www.interact -sw.co.uk / iangblog / 2004/11/12 / отмена

1 голос
/ 06 июня 2011

Во многих случаях (но определенно не во всех) вы могли бы защищаться от ThreadAbortException.Большая часть критического кода в .NET BCL уже достаточно хорошо справляется с этой задачей.Проблема в том, что действительно трудно понять правильно.И по этой причине большинство людей рекомендуют, и это правильно, избегать прерывания потоков.Начиная с версии 2.0 CLR сделал прерывания потока намного более терпимыми и представил новый набор API, чтобы помочь авторам кода защититься от них.Взгляните на Области ограниченного выполнения , чтобы глубже взглянуть на то, как все это работает.

Я полагаю, что вы правильно относитесь к своим проблемам на примере блока using.Для корректной работы ограниченных областей выполнения исключение внеполосного (асинхронного) должно происходить из блока try.Но из-за способа расширения using выражение вычисляется вне блока try.Сравните это с расширением блока lock, который оценивает выражение из блока try.Что ж, это правда с версией 4.0 платформы в любом случае, и это было изменено специально для защиты от этих исключений.

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

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

1 голос
/ 06 июня 2011

Есть компромисс.

  1. Обязательно немедленно закрывайте все ресурсы, даже при наличии ThreadAbortException
  2. Имеют более простой код, но временно теряют ресурсы, если Abort () вызывается

Я предполагаю, что вы не звоните Abort, а просто хотите быть в безопасности, если это делает кто-то другой.Если вы звоните Abort, я бы посоветовал вам этого не делать.Это не единственная проблема, с которой вы столкнетесь.Есть и другие проблемы с Abort в документации .

# 2 является допустимым выбором, потому что вызывающие Abort () должны ожидать этого.

Если вы хотите выбрать #1, тогда я не думаю, что даже простая попытка / уловка поможет.Если ThreadAbortException может происходить везде, то это может произойти и после открытия файла (внутри File.OpenWrite ()) и до того, как вы можете назначить его переменной, для которой вы можете вызвать Dispose () - у вас будет та же проблемакак в вашем коде.

Вам нужна семантика типа

  using (var handle = GetUnOpenedHandle()) {
        handle.Open(); // this can't involve assignment to any field of handle
  }

Я не уверен, что это возможно.

0 голосов
/ 06 июня 2011

Прерывание потока чаще всего используется в случае фатальной ошибки, поэтому ваш ответ, вероятно, должен позволить вашему приложению завершиться.Если вы пытаетесь остановить собственные потоки, используйте Thread.Join ().

...