2013-08-01 9 views
5

Все, что я прочитал, утверждает, что прерывание в потоке выполнит блок finally до окончания исключения ThreadAbortException. Я хотел подтвердить это, поэтому я могу планировать, как обращаться с некоторым сторонним кодом, который может вешать бесконечно. Однако следующий тест меня путать:Может ли ThreadAbortException пропустить наконец?

public void runTest(DateTime deadline) 
{ 
    testThread = new Thread(() => 
    { 
     try 
     { 
      Console.WriteLine("test thread started at " + DateTime.Now.ToShortTimeString()); 
      while (true) { } 
     } 
     finally 
     { 
      Console.WriteLine("test thread entered FINALLY at " + DateTime.Now.ToShortTimeString()); 
      while (true) { } 
     } 
    }); 
    testThread.Start(); 
    while (testThread.IsAlive && deadline.Subtract(DateTime.Now).TotalSeconds > 0) 
    { 
     Console.WriteLine("main thread while loop " + DateTime.Now.ToShortTimeString()); 
     Thread.Sleep(10000); 
    } 
    if (testThread.IsAlive) 
     testThread.Abort(); 
    Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString()); 
} 

Что я нахожу при запуске в том, что консоль никогда не упоминает о входе окончательно заблокировать. Приложение продолжается после вызова .Bort, как будто никакого окончательного блока вообще нет. Я делаю что-то неправильно? Не следует ли переходить к блоку finally до того, как он достигнет окончательной записи на консоль, или же порядок выполнения все еще является функцией того факта, что, наконец, в отдельном потоке или что-то еще?

+0

Я запустил код, и я получил «тестовый поток, введенный ПОЛНОСТЬЮ в 3:21 PM». – Romoku

+0

Не пробовал, но я подозреваю, что код будет вести себя по-разному между отладчиками Debug и release/без отладчика, как бесконечный цикл, который, вероятно, будет JITed во что-то, что не может быть прервано в сборке релизов. –

+2

Вызов 'Thread.Abort' - это как остановка автомобиля, снимая водителя в голове. Машина остановится, но пока не известно, что может произойти за это время. Вы действительно не должны * когда-либо * называть 'Thread.Abort'. Если да, то вам нужно пересмотреть дизайн вашего приложения. –

ответ

6

Docs say: ThreadAbortException - особое исключение, которое может быть уловлено, но оно будет автоматически поднято снова в конце блока catch. Когда это исключение возникает, среда выполнения выполняет все блоки finally до окончания потока. Поскольку поток может выполнить неограниченное вычисление в блоках finally или вызвать Thread.ResetAbort, чтобы отменить прерывание, нет гарантии, что поток когда-либо закончится.

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

Это или у вас есть условие гонки, так как потоки выполняются параллельно: основной поток заканчивается до того, как тестовая нить может выдавать окончательные данные (Исключения стоят дорого и требуют времени, чтобы достичь уловов или блоков) ,

+3

Запускаемые темы обычно не собираются. См.: Http://stackoverflow.com/questions/81730/what-prevents-a-thread-in-c-sharp-from-being-collected –

+0

Мой опыт отличается, однако, похоже, результат - это состояние гонки между основной поток и порожденная нить? Обновленный мой ответ с этим, спасибо за вход @ebyrob – Haney

+0

Я попробовал это снова несколько раз после того, как Ромоку упомянул, что это сработало для него. Это подтверждает мое подозрение, что нить не обязательно прервана до того, как управление вернется в основной поток. Я добавил .join после выхода из строя, и теперь он остается в конце, не называя окончательную консольную запись в основном потоке. Так что DavidH прав, их состояние гоночного состояния есть здесь. – user1807768

2

finally обычно не следует пропускать.

Возможно, что консольное приложение (при условии, что оно одно) выходит до запуска блока finally (так как после звонка Thread.Abort() нет ожидания).

Что произойдет, если вы положите Console.ReadLine() в конце программы?

5

Блок finally в функции рабочего потока выполняется на рабочем потоке, который параллелен основному потоку. Это состояние гонки. Вы не можете определить, какой из рабочих потоков наконец или код основного потока после того, как абонентский вызов будет выполнен раньше. Если вам необходимо синхронное прерывание, то вы должны поставить что-то вроде этого:

 if (testThread.IsAlive) 
     { 
      testThread.Abort(); 

      bool blnFinishedAfterAbort = testThread.Join(TimeSpan.FromMilliseconds(1000)); 
      if (!blnFinishedAfterAbort) 
      { 
       Console.WriteLine("Thread abort failed."); 
      } 
     } 
     Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString()); 

Имейте в виду, что если у вас есть обработка наследство исключение включена (см http://msdn.microsoft.com/en-us/library/ms228965.aspx) и вы указали AppDomain_UnahandledException обработчик событий, то ThreadAbortException будет выполните выполнение этого обработчика ПЕРЕД последним блоком в функции рабочего потока. Это просто еще один пример разочарования и неожиданного исполнения, о котором нужно знать, прерывая потоки.