Завершение потоков в C#

При вызове Thread.Abort рассматриваемый поток, в конце концов, получает ThreadAbortException. Поэтому, естественно, для аккуратного обслуживания этой ситуации необходимо обрабатывать исключение ThreadAbortException, если поток должен делать что-то особенное в случае его прерывания.

Существует даже перегрузка Abort, которая принимает произвольную объектную ссылку, инкапсулируемую затем в ThreadAbortException. Это позволяет коду, прерывающему поток, передать некоторую контекстную информацию обработчику ThreadAbortException, например, причину вызова Abort.

CLR не доставляет ThreadAbortException, если только поток не выполняется внутри управляемого контекста. Если поток вызвал “родную” функцию через уровень P/Invoke, и эта функция требует длительного времени на выполнение, то прерывание потока откладывается до тех пор, пока управление не вернется в управляемое пространство.

В .NET 2.0 и последующих версиях при выполнении блока finally доставка ThreadAbortException откладывается до тех пор, пока выполнение не покинет блок finally. В .NET 1.x исключение прерывания доставляется в любом случае.

Вызов Abort на потоке не прерывает его принудительно, поэтому если нужно ждать до тех пор, пока поток действительно не завершит выполнение, понадобится вызвать Join на этом потоке, чтобы подождать до тех пор, пока не завершится код обработчика исключения ThreadAbortException.

Во время этого ожидания имеет смысл установить таймаут, чтобы не ждать вечно, пока поток завершит уборку за собой. Несмотря на то что код в обработчике исключений должен подчиняться другим правилам кодирования обработчиков, существует вероятность, что обработчику понадобится немало времени, а то и бесконечно много, чтобы завершить работу.

Давайте посмотрим, как функционирует обработчик ThreadAbortException:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Threading;
public class EntryPoint
{
private static void ThreadFunc ()  { 
ulong counter = 0; while ( true )  { 
try {
Console.WriteLine ( "{0}", counter++ );
}
catch ( ThreadAbortException )  {
// Попытка проглотить исключение и продолжиться. 
Console.WriteLine ( "Прекратить!" );
}
static void Main()  { 
Thread newThread =
new Thread( new ThreadStart(EntryPoint.ThreadFunc)  ); 
newThread.Start(); 
Thread.Sleep( 2000 ) ;
// Прервать поток. 
newThread.Abort();
// Ждать завершения потока. 
newThread.Join ();
}

После беглого взгляда на код может показаться, что вызов Join на экземпляре newinstance заблокирует выполнение навсегда. Однако этого не случается. Казалось бы, поскольку ThreadAbortException обрабатывается внутри цикла функции потока, исключение будет “проглочено” и цикл продолжится, независимо от того, сколько раз главный поток попытается прервать данный поток.

Однако исключение ThreadAbortException, сгенерированное через метод Thread.Abort, ведет себя особым образом.

Когда поток завершает обработку исключения, исполняющая среда заново неявно генерирует его в конце обработчика. Это все равно, как если бы вы сами повторно сгенерировали исключение. Таким образом, любые внешние обработчики или блоки finally будут выполняться нормально. В примере вызов Join не будет ждать вечно, как можно было предполагать.

Повторную генерацию системой исключения ThreadAbortException можно предотвратить, вызвав статический метод Thread.ResetAbort. Однако общая рекомендация предусматривает вызов ResetAbort только из потока, вызвавшего Abort. Если нужно, чтобы это происходило внутри обработчика исключения ThreadAbortException прерываемого потока, понадобится применить сложную технику внутрипотокового взаимодействия.

Если вы пришли к заключению о необходимости реализации такой техники отмены прерывания потока, то это, скорее всего, свидетельствует о том, что вам, прежде всего, стоит пересмотреть дизайн своей системы. Другими словами, это признак плохого дизайна!

Хотя исполняющая среда обеспечивает намного более ясный механизм для прерывающихся потоков, такой как информирование заинтересованных сторон о факте прерывания потока, все равно обработчик ThreadAbortException должен быть реализован правильно.

Тот факт, что экземпляры ThreadAbortException могут генерироваться асинхронно в произвольном управляемом потоке, затрудняет создание устойчивого, безопасного к исключениям кода.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100