C# - Thread Abort Exception (исключение прерывания потока) переосмысление себя
у меня есть текущий код:
class Program
{
private static void Main()
{
while (true)
{
try
{
Thread.CurrentThread.Abort();
}
catch (ThreadAbortException)
{
Console.WriteLine("Abort!");
Thread.ResetAbort();
}
Console.WriteLine("now waiting");
Console.ReadKey();
}
}
}
теперь я знаю метод ResetAbort
предполагается запретить ThreadAbortException
от продолжать повторно бросить себя, даже когда catch
заявление ловит его, но мой вопрос заключается в следующем:
если кто-нибудь может использовать ResetAbort
метод, тогда в чем смысл исключения специально повторно бросить себя?
пользователь может просто сделать
catch (ThreadAbortException ex)
{
Console.WriteLine("Abort!");
throw ex;
}
5 ответов
точки ThreadAbortException
перестроение само по себе должно убедиться, что поток завершается, если пользователь явно не вызывает ResetAbort
.
поясню:
try
{
// ... Thread abort happens somewhere in here
}
catch (Exception ex)
{
_log.Error(ex);
}
здесь у вас есть типичный пример кода, который гарантирует, что исключение не распространяется изнутри try
блок. Я знаю, что ловить Exception
- это плохая практика, но такой код тем не менее существует.
если вы называете Abort
в то время как поток находится внутри try
блок, который вы все еще хотите выкинуть. Вы просто не можете полагаться на пользователей, пишущих такой код везде:
try
{
// ... Thread abort happens somewhere in here
}
catch (ThreadAbortException)
{
throw; // No, you'll NEVER see code like this in real life
}
catch (Exception ex)
{
_log.Error(ex);
}
Итак, чтобы обеспечить вроде надежный Abort
, исключение должно быть автоматически перестроено, или оно может легко быть отброшено случайно.
ResetAbort
предназначен для очень редкого случая, когда вы специально обнаруживаете прерывание потока, и вы точно знаете, почему это произошло, и вы хотите предотвратить это.
Излишне говорить, что варианты использования для этого крайне редки. Прерывания потока обрабатываются средой выполнения особым образом, и вы должны избегать их, когда это возможно. Черт возьми, они даже не надежны, как вы указали, и все это обсуждение игнорирует CERs, которые ухудшают ситуацию.
Thread.ResetAbort()
не предназначен для общего использования. Это может вызвать нежелательное поведение, если вы не понимаете, почему произошел аборт thead. Из-за этого и, вероятно, чтобы сделать ASP.Сети стабильны в средах общего хостинга,SecurityPermissionFlag.ControlThread
разрешение требуется для вызова Thread.ResetAbort()
дело в том, чтобы определить поведение по умолчанию, в котором исключение перестраивается, видя, как есть удаленный шанс, что у пользователя будет какая-либо точка продолжения потока.
кроме того, ResetAbort
имеет требование безопасности и не может быть вызван каким-либо кодом.
если вы стремитесь создать приложение, которое демонстрирует когнитивные искажения или раздвоение личности поведение или какую-то собственную трансформации/мутации кода.. (что может быть очень крутым опытом - я знаю.. :))
нет смысла прерывать поток, чем ловить брошенное исключение и управлять им, либо сбрасывая аборт, либо повторно бросая его..
Я думаю, что вы используете неправильный сценарий, чтобы понять причину за эти инструкции.
Рассмотрим пример:
предположим, вы разрабатываете многопоточное приложение, которое включает в себя информацию о базе данных.
и.. Допустим, ваш пользователь поручил выполнить какое-то пакетное задание, связанное с обработкой и сохранением данных в вашей БД.
И.. эта задача-скажем, занимает 30 секунд.
И вот-вы открываете для него нить и управляете этим в фоновом режиме. (назовем это нитью "А")
между тем.. Ваш пользователь щелкнул, чтобы выйти из программы , и он сделал это, пока поток "A" все еще выполняется.
теперь ваш метод выхода - не очень управляется..
Он предназначен только для простого прерывания всех запущенных потоков, открытых вашим приложением.
Он не знает, что делает каждая нить..
так что теперь делать? - Есть три дисциплины:
выход сразу - пользователь-это святое! и если вот чего он хотел - Кто я такой, чтобы задавать ему вопросы.
выполнить задание из потока "A"и только после этого выйти..
откат по последней инструкции потока "A"и только потом выход..
В Вариант 1 целостность базы данных может быть нарушена из-за действий пользователя..
В Варианте 2 и 3 Вы отдали приоритет логическому потоку за счет отзывчивости на ваш запрос пользователя.
Итак, если вы не очень скромны.. вероятно, вы выберете вариант 2 или Вариант 3.
Но предполагая, что ваш метод выхода используется Thread.Abort()
,
Вам нужно будет использовать Thread.ResetAbort()
"перекроет" и завершить свою цель.
коротко говоря - это мера "благодати", чтобы позволить вам больше контролировать то, что происходит в некоторых сценариях.
обычно хорошая практика позволит методу выхода знать, что происходит на.
Обычно хорошей практике не нужно будет прерывать нить (она открылась сама) вообще.
заключение
да - приятно знать, что он есть... и нет - это плохо использовать.
если вы собираетесь использовать Thread.Abort()
на нить вы не открывали (возможно-сложно..),
Или ожидая, что будет прерван извне и поэтому понадобится Thread.ResetAbort()
.
иначе - нет необходимости использовать его.
потому что прерывание потока не обязательно означает, что исключение будет выдано. Для Процедура Прерывания на catch (ThreadAbortException)
block - Это еще одна критическая область кода. Это только дает нам потокобезопасный и удобный способ обнаружения, прерывается ли текущий поток (и, возможно, с некоторым состоянием), если мы хотим сделать что-то особенное. Кроме этого, он похож на любую другую критическую область (например, блок finally), где он завершит поток после его исполнение.
В то же время, в вашем примере Abort
называется синхронно (который на самом деле безопасно) и в этом случае это очень похоже на исключение. Вещи только становятся интересными и опасными, когда это называется асинхронно из другого потока из-за того, что процедура прерывания сложнее, чем просто исключение: по сути, сначала поток помечается как прерываемый, а затем критические области кода (например, finally blocks) выполняются, и только тогда возникает исключение, если AbortRequested флаг по-прежнему установлен в потоке и так далее.
код ниже иллюстрирует этот факт, восстанавливая прерванный поток без каких-либо исключений:
var inFinally = new ManualResetEvent(false);
var abortCalled = new ManualResetEvent(false);
var t = new Thread(_ =>
{
Console.WriteLine("Thread started..");
try
{
}
finally
{
inFinally.Set();
abortCalled.WaitOne();
Console.WriteLine(" ThreadState (before): " + Thread.CurrentThread.ThreadState);
// This isn't thread safe, and ugly?
if ((Thread.CurrentThread.ThreadState & ThreadState.AbortRequested) != 0)
{
Thread.ResetAbort();
}
Console.WriteLine(" ThreadState (after): " + Thread.CurrentThread.ThreadState);
}
Console.WriteLine("Executed because we called Thread.ResetAbort()");
});
t.Start();
inFinally.WaitOne();
// Call from another thread because Abort()
// blocks while in finally block
ThreadPool.QueueUserWorkItem(_ => t.Abort());
while ((t.ThreadState & ThreadState.AbortRequested) == 0)
{
Thread.Sleep(1);
}
abortCalled.Set();
Console.ReadLine();
// Output:
//--------------------------------------------------
// Thread started..
// ThreadState (before): AbortRequested
// ThreadState (after): Running
// Executed because we called Thread.ResetAbort()
Теперь, я должен быть честным: я не совсем уверен, как можно использовать эту функцию и создать что-то полезное. Но это похоже на нить.Abort API был (вероятно, все еще есть, я не знаю) используется для облегчение повторного использования потоков и AppDomain в таких фреймворках, как ASP.NET.
в одной из записей блога Джо Даффи,управляемый код и асинхронное исключение, он говорит о ResetAbort и API прерывания:
некоторая инфраструктура рамок, в частности ASP.NET, даже прерывает отдельные потоки обычно без выгрузки домена. Они останавливают ThreadAbortExceptions, звонят ResetAbort в потоке и повторно использовать его или вернуть его в CLR потоки ThreadPool.
Я могу себе представить, что его можно использовать в рамках для повторного использования управляемых потоков, уменьшая накладные расходы. Однако проблемы (плохой дизайн синхронизации потоков, плохая обработка исключений, мертвые блокировки и т. д.), введенные в пользовательский код этим легко неправильно понятым API, сделали вызовы Abort и ResetAbort более хлопотными, чем полезными.