Подтвердить что ты не робот

ThreadAbortException

Скажем, у нас есть некоторый код, подобный этому, в отдельном потоке:

private static void ThreadFunc() {
    ulong counter = 0;

    while (true) {

        try {
            Console.WriteLine( "{0}", counter++ );
        }
        catch (ThreadAbortException) {
            Console.WriteLine( "Abort!" );
        }

    }
}

Когда вызывается Thread.Abort(), возможно ли, что исключение выбрано вне блока catch?

4b9b3361

Ответ 1

На самом деле да, ThreadAbortException является особенным. Даже если вы справитесь с этим, он будет автоматически перезагружен CLR в конце try/catch/finally. (Как отмечено в комментариях, его можно подавить с помощью ResetAbort, но к этому моменту код пахнет гнилой рыбой.)

Не говоря уже о том, что нет очевидного исполняемого кода вне вашего try/catch/, наконец, каждая итерация цикла завершается за пределами области действия в течение небольшой продолжительности, поэтому прерывание может происходить вне вашего блока try.

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

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

Ответ 2

Да. Я подозреваю, что вы спрашиваете, потому что прерывания потока происходят только тогда, когда поток может в противном случае заблокировать (или если он уже заблокирован) - например. для IO.

Нет такой гарантии для прерывания. Это может произойти в любое время, в основном, хотя существуют регионы с задержкой, такие как ограниченные области выполнения и блоки catch/finally, где запрос прерывания просто запоминается, и поток прерывается, когда он выходит из области.

Синхронные прерывания потока (т.е. прерывание собственного потока) достаточно безопасны, но асинхронные прерывания (прерывание другого потока) почти всегда являются плохими идеями. Прочтите "Параллельное программирование в Windows" Джо Даффи для получения дополнительной информации.

EDIT: Как отмечено Эриком ниже, отменить другой поток также не гарантирует никакого эффекта. Просто процитировать комментарий:

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

Ответ 3

Собственно, ThreadAbortException является особым в случае, если он вызывается методом CLR или Thread.Abort. Сравнить вывод:

  • Немного измененный пример (добавлен Console.WriteLine) от Джо Даффи "Параллельное программирование в Windows". Он выдает исключение Thread.CurrentThread.Abort:
    
    try
    {
        try
        {
            Thread.CurrentThread.Abort();
        }
        catch (ThreadAbortException)
        {
            Console.WriteLine("First");
            //Try to swallow it.
        } //CLR automatically reraises the exception here .
    }
    catch (ThreadAbortException)
    {
        Console.WriteLine("Second");
        Thread.ResetAbort();
        //Try to swallow it again .
    } //The in - flight abort was reset , so it is not reraised again .
    
    
    
    Вывод:
    First
    Second
    
  • Измените предыдущий пример, чтобы использовать другой подход создания экземпляра ThreadAbortException:
    
    try
    {
        try
        {
            // get non-public constructor
            var cstor = typeof(ThreadAbortException)
                .GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
            // create ThreadAbortException instance
            ThreadAbortException ex = cstor.Invoke(null) as ThreadAbortException;
    
            // throw..
            throw ex;
        }
        catch (ThreadAbortException)
        {
            Console.WriteLine("First");
        } 
    }
    catch (ThreadAbortException)
    {
        Console.WriteLine("Second");
        Thread.ResetAbort();
    } 
    
    Вывод:
    First
    

Похоже, что методы Thread вызывают внутренний код внутри, что делает особый особый исключение.

Ответ 4

Я не на 100%, что вы просите, но я хотел бы указать, что вы никогда не сможете усвоить ThreadAbortException

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

Вы спрашиваете, можно ли поймать ThreadAbortException, который был добавлен в другой поток с помощью try/catch? Если это ваш вопрос, то нет, вы не можете.