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()

MSDN Link


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

кроме того, ResetAbort имеет требование безопасности и не может быть вызван каким-либо кодом.


если вы стремитесь создать приложение, которое демонстрирует когнитивные искажения или раздвоение личности поведение или какую-то собственную трансформации/мутации кода.. (что может быть очень крутым опытом - я знаю.. :))

нет смысла прерывать поток, чем ловить брошенное исключение и управлять им, либо сбрасывая аборт, либо повторно бросая его..

Я думаю, что вы используете неправильный сценарий, чтобы понять причину за эти инструкции.

Рассмотрим пример:

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

и.. Допустим, ваш пользователь поручил выполнить какое-то пакетное задание, связанное с обработкой и сохранением данных в вашей БД.
И.. эта задача-скажем, занимает 30 секунд.
И вот-вы открываете для него нить и управляете этим в фоновом режиме. (назовем это нитью "А")

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

теперь ваш метод выхода - не очень управляется..
Он предназначен только для простого прерывания всех запущенных потоков, открытых вашим приложением.
Он не знает, что делает каждая нить..

так что теперь делать? - Есть три дисциплины:

  1. выход сразу - пользователь-это святое! и если вот чего он хотел - Кто я такой, чтобы задавать ему вопросы.

  2. выполнить задание из потока "A"и только после этого выйти..

  3. откат по последней инструкции потока "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 более хлопотными, чем полезными.