Может ли ThreadAbortException вызываться в середине блока finally? - PullRequest
4 голосов
/ 09 ноября 2019

Предполагается, что следующий код выполняется в отдельном потоке:

try{
  var stuffToDispose = new SomeClassThatNeedsDisposing();
  //doing thing with stuffToDispose
}
finally{
   if(stuffToDispose != null)
     stuffToDispose.Dispose();
}

, и этот поток прерывается с использованием механизма, вызывающего ThreadAbortException. Может ли ThreadAbortException произойти между нулевой проверкой и Dispose ()? Ака, в середине блока finally?

finally{
   if(stuffToDispose != null)
     //This is where the Exception would strike
     stuffToDispose.Dispose();
}

Я почти уверен, что ответ - нет, но кто-то, похоже, убежден, что это возможно.

Ответы [ 3 ]

4 голосов
/ 09 ноября 2019

В .NET Framework ThreadAbortException обрабатывается особым образом как в блоках catch, так и finally:

  • Если пойман ThreadAbortException,он автоматически повторно поднимается в конце блока catch (если он не подавлен Thread.ResetAbort)
  • В блоке finally ThreadAbortException не будет активирован до концавсего блока. Это намеренное поведение. И поэтому иногда можно найти скрытые коды с пустым блоком try {}, где все находится в разделе finally. Это гарантирует, что этот раздел не будет прерван.

С другой стороны, .NET Core не поддерживает Thread.Abort (выдает PlatformNotSupportedException). Сам ThreadAbortException не был удален (по соображениям совместимости), так что вы все равно можете выбросить его явно, но я не думаю, что он все еще обрабатывается, как описано выше (я не проверял его).

2 голосов
/ 09 ноября 2019

Наконец, не прервется, если

Запуск этого кода в .NET 4.0.0-4.7.2 будет бесконечно блокировать при Thread.Abort() печати foo и в конечном итоге:

class Program
{
    static void Main(string[] args)
    {
        var i = false;
        var t = new Thread(() =>
        {
            try { Console.Write("foo"); } finally { while (true) { i=true; } }
        });
        t.Start();
        Thread.Sleep(200);
        t.Abort();
        t.Join();
        Console.WriteLine("bar = {0}", i);
        Console.ReadKey();
    }
}

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

Наконец, будет выполнено, но прервано, если

При запуске того же раздела в .NET 2.0-3.5 будет напечатано foobar = True, что означает, что оно прервано.

Но если вы измените его следующим образом:

        var t = new Thread(() =>
        {
            try { Console.Write("foo"); } finally { while (true) { Console.Write(".");} }
        });

Он будет работать бесконечно, как в первом примере. Это ... странно.

Резюме

Лучше прямо спросить об этом в Microsoft.

1 голос
/ 09 ноября 2019

Согласно моим тестам, вызов Thread.Abort не прерывает текущий запущенный блок finally внутри потока.

Код теста:

var thread = new Thread(() =>
{
    try
    {
    }
    catch (ThreadAbortException)
    {
        Console.WriteLine("ThreadAbortException");
        throw;
    }
    finally
    {
        Console.WriteLine("Thread Finally Started");
        Thread.Sleep(200);
        Console.WriteLine("Thread Finally Finished");
    }
})
{ IsBackground = true };
thread.Start();
Thread.Sleep(100);
Console.WriteLine("Aborting...");
thread.Abort();
thread.Join();

Вывод (.NET Framework 4.8):

Нить окончательно запущена
Прерывание ...
Нить окончательно завершена

Имейте в виду, что Thread.Abort не поддерживаетсяв .NET Core.

System.PlatformNotSupportedException: прерывание потока не поддерживается на этой платформе.

...