2015-07-22 7 views
1

Рассмотрим следующий сценарийПовторное выбрасывание исключения в задаче не делает задачу идти в поврежденном состоянии

var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(2)); 
     var startNew = Task.Factory.StartNew(() => 
     { 
      var currentThread = Thread.CurrentThread; 
      try 
      { 
       using (cancellationTokenSource.Token.Register(currentThread.Abort)) 
        new AutoResetEvent(false).WaitOne(Timeout.InfiniteTimeSpan); 
      } 
      catch (ThreadAbortException abortException) 
      { 
       throw new TimeoutException("Operation timeouted", abortException); 
      } 
     }, cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current); 
     startNew.ContinueWith(val => Console.WriteLine("Cancellation handled"), TaskContinuationOptions.OnlyOnCanceled); 
     startNew.ContinueWith(val => Console.WriteLine("Fault handled"), TaskContinuationOptions.OnlyOnFaulted); 
     startNew.ContinueWith(val => Console.WriteLine("Ran to completion handled"), TaskContinuationOptions.OnlyOnRanToCompletion); 

Отложив все обсуждение, что их прерывание нити есть зло, почему этот код не делает задачу перейдите в состояние Faulted? Однако удаление поймать блок или позвонив по телефону

Thread.ResetAbort() 

, кажется, сделать трюк

ответ

2

Речь идет о том, как нить прервать работы:

try { 
    try { 
     try { 
      Thread.CurrentThread.Abort(); 
     } catch(Exception e) { 
      Console.WriteLine(e.GetType()); 
      throw new Exception(); 
     } 
    } catch(Exception e) { 
     Console.WriteLine(e.GetType()); 
    } 
} catch(Exception e) { 
    Console.WriteLine(e.GetType()); 
} 

Этот код выводит:

System.Threading.ThreadAbortException 
System.Exception 
System.Threading.ThreadAbortException 

Итак, когда ваше пользовательское исключение будет обработано, ThreadAbortException будет повторно выбрано.

ThreadAbortException является исключением, которое может быть перехвачено кодом приложения, но повторно брошено в конце catch блока, если ResetAbort не называется. MSDN

Теперь давайте посмотрим, в какой-то source:

/// <summary> 
/// Executes the task. This method will only be called once, and handles bookeeping associated with 
/// self-replicating tasks, in addition to performing necessary exception marshaling. 
/// </summary> 
private void Execute() 
{ 
    if (IsSelfReplicatingRoot) 
    { 
     ExecuteSelfReplicating(this); 
    } 
    else 
    { 
     try 
     { 
      InnerInvoke(); 
     } 
     catch (ThreadAbortException tae) 
     { 
      // Don't record the TAE or call FinishThreadAbortedTask for a child replica task -- 
      // it's already been done downstream. 
      if (!IsChildReplica) 
      { 
       // Record this exception in the task's exception list 
       HandleException(tae); 

       // This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to 
       // skip the regular Finish codepath. In order not to leave the task unfinished, we now call 
       // FinishThreadAbortedTask here. 
       FinishThreadAbortedTask(true, true); 
      } 
     } 
     catch (Exception exn) 
     { 
      // Record this exception in the task's exception list 
      HandleException(exn); 
     } 
    } 
} 

Как вы можете видеть, есть специальный codepath для ThreadAbortException случае для транзитной задачи в поврежденном состоянии. Поскольку вы скрываете ThreadAbortException от TimeoutException, эта специальная кодировка не принимается. Поэтому, когда регулярное кодирование обрабатывает исключение, записывая его в список исключений задачи, ThreadAbortException будет повторно выбрано, что предотвратит правильный переход задачи в состояние ошибки.