Автор: Sergey Teplyakov
Изучение ThreadAbortException с помощью Rotor, Крис Селлз
Потоки и исключения
static void Foo() {
throw new Exception(oops!),
Console.WriteLine(Never going to get here...),
}
static void Main(string[] args) {
try {
Foo(),
Console.WriteLine(Never going to get here, either...),
}
catch( Exception ex ) {
Console.WriteLine(Exceptions happen: + ex.Message),
}
}
static void Foo() {
try { while( true ) { ... } }
catch( ThreadAbortException ex ) { ... }
finally {...}
// Will never get here if thread aborted
}
static void Main(string[] args) {
Thread thread = new Thread(new ThreadStart(Foo)),
thread.Start(),
thread.Abort(), // cause ThreadAbortException to be thrown
}
Детали реализации
- 1. Приостанавливает прерываемый поток операционной системы. 2. Устанавливает бит AbortRequested .NET потока. 3. Ожидает перехода прерываемого потока в состояние, в котором возможно его прерывание (interruptible state), путем вызова функций Sleep, Join или wait-функций. 4. Добавляет APC-вызов (Asynchronous procedure call) в очередь APC потока (используя Win32 функцию QueueUserAPC) и возобновляет выполнение потока. 5. Когда прерываемый поток переходит в тревожное состояние (alertable wait state), планировщик вызвает обработчик APC, который устанавливает состояние прерываемого потока в AbortInitiated. Поток переходит в тревожное сотояние только при передаче TRUE в качестве значения bAlertable в функцию типа SleepEx. Если этого не происходит, очередь APC не обработает запрос, а это значит, что вызов ThreadAbort не гарантированно приведет к генерации исключения ThreadAbortException в прерываемом потоке. 6. Когда Common Language Runtime (CLR) возвращает управление прерываемому потоку через обходную (trip) функцию, которая проверяет все возможные состояния потока для специальных действий, включая будет ли поток прерван. 7. Если состояние потока установлено в AbortInitiated, генерируется исключение ThreadAbortEception, которое может быть обработано (или не обработано) прерываемым потоком в блоках catch и/или finally.
Вызов Thread.Abort приводит к установке CLR определенного флага прерываемого потока, затем этот флаг проверяется в определенных контрольных точках жизни потока, после чего генерируется исключение, если этот флаг был выставлен.
Как я это выяснил
Я выяснил как Thread.Abort приводит к генерации исключения из одного потока в другой путем загрузки и тщательного изучения исходного кода Rotor [1]. Хотя потоки являются низкоуровневыми примитивами (в отличие от WinForms или ASP.NET), исходный код Rotor содержит реализацию и позволяет нам понять, как на самом деле работают те или иные вещи (например, что происходит когда прерывается поток, выполняющий блоки catch или finally).
namespace System.Threading {
public sealed class Thread {
...
public void Abort() { AbortInternal(), }
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern void AbortInternal(),
}
}
static
ECFunc gThreadFuncs[] = {
...
{FCFuncElement(AbortInternal, NULL, (LPVOID)ThreadNative::Abort)},
...
},
Где же мы сейчас?
Генерация исключения из одного потока в другой работает прекрасно, поскольку CLR заботиться обо всем за нас и не требует реализации этой возможности собственными силами (как в случае работы с Win32). Это еще один пример виртуализации платформы на пути к обеспечению сервисов, которые не предоставляются нижележащей операционной системой. И как я это все выяснил? Все это я выяснил с помощью лучшей документации любого программного обеспечения, имеется ввиду, конечно же, исходный код.Ссылки
- Shared Source CLI 1.0 Release (AKA Rotor source code) Rotor: Shared Source CLI Provides Source Code for a FreeBSD Implementation of .NET, Jason Whittington, MSDN Magazine, July 2002 Shared Source CLI Essentials, David Stutz et al, O'Reilly and Associates, March 2003 (est.)