Как я могу обнаружить исключение ThreadAbortException в блоке finally? (.СЕТЬ)

У меня есть некоторая критическая логика в блоке finally (с пустым блоком try), потому что я хочу гарантировать, что код будет выполнен, даже если поток будет прерван. Однако я также хотел бы обнаружить исключение ThreadAbortException. Я обнаружил, что упаковка моего критического блока try / finally в try / catch не перехватывает исключение ThreadAbortException. Есть ли способ обнаружить это?

пытаться {
    пытаться { }
    в конце концов {
        // критическая логика
    }
} catch(Exex ex) {
    // ThreadAbortException здесь не перехватывается, но генерируются исключения
    // изнутри критической логики
}

7 ответов

Решение

Это любопытная проблема.

Код, который вы разместили, должен работать. Кажется, происходит какая-то оптимизация, которая решает не вызывать ваш обработчик catch.

Итак, я хотел обнаружить исключение с помощью этого:

bool threadAborted = true;
try {
  try { }
  finally { /* critical code */ }
  threadAborted = false;
}
finally {
  Console.WriteLine("Thread aborted? {0}", threadAborted);
}
Console.WriteLine("Done");

(Мой реальный код просто спал в этом критическом разделе кода, так что я мог быть уверен, что после этого он наконец прекратится.)

Это напечатано:

Тема прервана? Ложь

Хм, странно на самом деле!

Поэтому я подумал о том, чтобы проделать еще немного работы, чтобы обмануть любую "умную" оптимизацию:

bool threadAborted = true;
try {
  try { }
  finally { /* critical code */ }
  threadAborted = AmIEvil();
}
finally {
  Console.WriteLine("Thread aborted? {0}", threadAborted);
}
Console.WriteLine("Done");

куда AmIEvil просто:

[MethodImpl(MethodImplOptions.NoInlining)]
static bool AmIEvil() {
  return false;
}

Наконец это напечатано:

Тема прервана? Правда

И там у вас есть это. Используйте это в своем коде:

try {
  try { }
  finally { /* critical code */ }
  NoOp();
}
catch (Exception ex) {
  // ThreadAbortException is caught here now!
}

куда NoOp просто:

[MethodImpl(MethodImplOptions.NoInlining)]
static void NoOp() { }

На самом деле вы можете выполнить код в операторе catch просто отлично для исключения ThreadAbortException. Проблема в том, что исключение будет переброшено, как только выполнение покинет блок catch.

Если вы хотите фактически остановить продолжение исключения, вы можете вызвать Thread.ResetAbort(). Это требует полного доверия, и если у вас нет определенного сценария, это почти наверняка неправильно.

ThreadAbortException

Я не думаю, что это возможно.

Зачем вам нужно обрабатывать исключение ThreadAbortException? Вызов thread.Abort() обычно является признаком плохого дизайна. Иметь переменную flag, которая при значении true будет просто возвращать; из функции потока, после соответствующей очистки, конечно.

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

Если вызов Thread.Abort плохой дизайн, почему SQL Server вызывает его в потоке, на котором выполняется код пользователя? Потому что именно так обрабатывается отмена запроса, и это вызывает кошмары.

Я согласен с Арулом. Вызов Thread.Abort() является признаком плохого дизайна.

Позвольте мне процитировать Питера Ричи из MSDN: Thread.Abort (акцент мой):

Есть много причин не использовать Thread.Abort и ThreadAbortException

На некоторых платформах (например, x64 и IA64) прерывание может произойти до Monitor.Enter и блока try (даже с блокировкой /SyncLock), оставляя монитор потерянным. ThreadAbortException может возникать в стороннем коде, не написанном для обработки прерывания потока. Поток может быть прерван во время обработки блока finally в.NET 1.x. Используются исключения для нормальной логики потока управления. Асинхронное исключение может прервать изменение состояния или ресурсов сегмента, оставляя их поврежденными.

Для более подробной информации смотрите:
http://msmvps.com/blogs/peterritchie/archive/2007/08/22/thead-abort-is-a-sign-of-a-poorly-designed-program.aspx
http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
http://blogs.msdn.com/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx

Вы пробовали что-то подобное?

try {
    try { }
    catch (ThreadAbortException)
    {
      ThreadAbortExceptionBool = true;
    }
    finally {
        // critical logic
        if (ThreadAbortExceptionBool)
          // Whatever
    }
} 
catch(Exception ex) {
    // ThreadAbortException is not caught here, but exceptions thrown
    // from within the critical logic are
}
Другие вопросы по тегам