Завершення потоків в 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:

Після побіжного погляду на код може здатися, що виклик Join на екземплярі newinstance заблокує виконання назавжди. Однак цього не трапляється. Здавалося б, оскільки ThreadAbortException обробляється всередині циклу функції потоку, виключення буде "проковтну" і цикл триватиме, незалежно від того, скільки разів головний потік спробує перервати даний потік.

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

Коли потік завершує обробку виключення, виконуючого середовища заново неявно генерує його в кінці обробника. Це все одно, як якщо б ви самі повторно згенерували виняток. Таким чином, будь-які зовнішні обробники або блоки finally будуть виконуватися нормально. У прикладі виклик Join не чекатиме вічно, як можна було припускати.

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

Якщо ви прийшли до висновку про необхідність реалізації такої техніки скасування переривання потоку, то це, швидше за все, свідчить про те, що вам, перш за все, варто переглянути дизайн своєї системи. Іншими словами, це ознака поганого дизайну!

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

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