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

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

try {
    try { }
    finally {
        // critical logic
    }
} catch(Exception ex) {
    // ThreadAbortException is not caught here, but exceptions thrown
    // from within the critical logic are
}

Ответ 1

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

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

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

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

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

Отпечатано:

Тема отменена? False

Хммм, странно!

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

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;
}

Наконец, он напечатал:

Тема отменена? True

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

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

Где NoOp справедливо:

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

Ответ 2

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

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

ThreadAbortException

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

Позвольте мне привести Peter Ritchie из 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

Ответ 7

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

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
}