2. Как проглотить ThreadAbortException?
Иногда в коде нужно остановить выполнение одного из потоков. Для этого можно использовать метод
thread.Abort(). При вызове метода Abort в останавливаемом потоке выбрасывается исключение ThreadAbortException.Вы можете легко поймать его с помощью блока исключений, но, если вы не сбросите его, оно будет автоматически проброшено выше по стеку.
var thread = new Thread(() => {
try { … }
catch (ThreadAbortException e) {
…
}
});
…
thread.Abort();
Если всё-таки нужно обработать ThreadAbort и выполнить еще какие-то действия в останавливаемом потоке, то можно использовать метод Thread.ResetAbort(). Он прекращает процесс остановки потока и исключение перестаёт прокидываться выше по стеку. Важно понимать, что метод thread.Abort() сам по себе ничего не гарантирует — код в останавливаемом потоке может препятствовать остановке.Еще одна особенность
thread.Abort() заключается в том, что он не сможет прервать код в том случае, если он находится в блоках catch и finally. Внутри кода фреймворка часто можно встретить методы, у которых блок try пустой, а вся логика находится внутри finally. Это делается как раз с той целью, чтобы этот код не мог быть прерван через ThreadAbortException.Также вызов метода
thread.Abort() дожидается выброса ThreadAbortException. Объединим эти два факта и получим, что метод thread.Abort() может заблокировать вызывающий поток:var thread = new Thread(() =>В реальности с этим можно столкнуться при использовании конструкции
{
try { }
catch {
//ThreadAbortException не выбрасывается в catch
}
finally {
//ThreadAbortException не выбрасывается в finally
Thread.Sleep(-1);
}
});
thread.Start();
…
thread.Abort(); //Никогда не вернётся
using. Она разворачивается в try/finally, внутри finally вызывается метод Dispose. Он может быть сколь угодно сложным, содержать вызовы и обработчики событий, использовать блокировки. И если thread.Abort был вызван во время выполнения Dispose, то thread.Abort будет его ждать. Так мы получаем блокировку почти на пустом месте.В .NET Core метод
thread.Abort() выбрасывает PlatformNotSupportedException. И это очень хорошо, потому что мотивирует пользоваться не thread.Abort(), а неинвазивными методами остановки выполнения кода, например с помощью CancellationToken.Источники:
- Adam Furmanek «.NET Internals Cookbook» - https://blog.adamfurmanek.pl/
- Евгений Пешков «Особые исключения в .NET» - https://youtu.be/WLSrYgMWif4