Как я могу обнаружить исключение 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
Ответ 3
Читайте о Ограниченные области выполнения.
В частности, здесь будет полезен RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup.
Ответ 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
}