2013-09-30 4 views
1

У меня есть объект A, из которого я хочу выполнить новый поток с помощью метода B объекта. Я могу использовать Task.CreateNew и т. Д. Проблема в том, что я не знаю, как обрабатывать исключение в новом потоке.Обработка исключений для отдельных потоков

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

  • я не могу добавить код главного контура на всех

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

Есть ли способ достичь этого?

В коде ниже я не имею обработки исключений и мастер-нить продолжает:

static void Main() 
{ 
    Console.WriteLine("start"); 
    Task.Factory.StartNew(PrintTime, CancellationToken.None); 

    for (int i = 0; i < 20; i++) 
    { 
     Console.WriteLine("master thread i={0}", i + 1); 
     Thread.Sleep(1000); 
    } 
} 

private static void PrintTime() 
{ 
    for (int i = 0; i < 10; i++) 
    { 
     Console.WriteLine("inner thread i={0}",i+1); 
     Thread.Sleep(1000); 
    } 

    throw new Exception("exception"); 
} 

ответ

1

ниже решение работает независимо от того, какой поток генерирует исключение в первом:

static void Main() 
{ 
    Console.WriteLine("start"); 

    var innerCts = new CancellationTokenSource(); 
    Exception mainException = null; 
    var mainThread = new Thread(() => SafeMainThread(innerCts, ref mainException)); 
    mainThread.Start(); 

    var innerTask = Task.Factory.StartNew(state => PrintTime(state), 
              innerCts, 
              innerCts.Token, 
              TaskCreationOptions.LongRunning, 
              TaskScheduler.Default); 

    var innerFault = innerTask.ContinueWith(t => { Console.WriteLine("Inner thread caused " + t.Exception.InnerException.GetType().Name + ". Main thread is being aborted..."); mainThread.Abort(); }, 
              TaskContinuationOptions.OnlyOnFaulted); 

    var innerCancelled = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread cancelled."), 
               TaskContinuationOptions.OnlyOnCanceled); 

    var innerSucceed = innerTask.ContinueWith(_ => Console.WriteLine("Inner thread completed."), 
               TaskContinuationOptions.OnlyOnRanToCompletion); 

    try 
    { 
     innerTask.Wait(); 
    } 
    catch (AggregateException) 
    { 
     // Ignore. 
    } 

    mainThread.Join(); 

    Console.ReadLine(); 
} 

private static void SafeMainThread(CancellationTokenSource innerCts, ref Exception mainException) 
{ 
    try 
    { 
     MainThread(); 
     Console.WriteLine("Main thread completed."); 
    } 
    catch (ThreadAbortException) 
    { 
     Console.WriteLine("Main thread aborted."); 
    } 
    catch (Exception exception) 
    { 
     Console.WriteLine("Main thread caused " + exception.GetType().Name + ". Inner task is being canceled..."); 

     innerCts.Cancel(); 
     mainException = exception; 
    } 
} 

private static void MainThread() 
{ 
    for (int i = 0; i < 20; i++) 
    { 
     Console.WriteLine("master thread i={0}", i + 1); 
     Thread.Sleep(500); 
    } 
    throw new Exception("exception"); 
} 

private static void PrintTime(object state) 
{ 
    var cts = (CancellationTokenSource)state; 

    for (int i = 0; i < 10; i++) 
    { 
     cts.Token.ThrowIfCancellationRequested(); 

     Console.WriteLine("inner thread i={0}", i + 1); 
     Thread.Sleep(500); 
    } 

    throw new Exception("exception"); 
} 
2

Сохранить ссылку на экземпляр задачи, и вызвать Wait на него, когда вы будете готовы, чтобы обработать его результат. Любое необработанное внутреннее исключение, которое было выбрано во время выполнения задачи, было бы обернуто в AggregateException, которое, в свою очередь, было выбрано из метода Wait.

static void Main() 
{ 
    Console.WriteLine("start"); 
    Task task = Task.Factory.StartNew(PrintTime, CancellationToken.None); 

    for (int i = 0; i < 20; i++) 
    { 
     Console.WriteLine("master thread i={0}", i + 1); 
     Thread.Sleep(1000); 

     // Stop iterating in case of unhandled exception in inner task. 
     if (task.Status == TaskStatus.Faulted) 
      break; 
    } 

    try 
    { 
     task.Wait(); 
    } 
    catch (AggregateException ae) 
    { 
     ae.Handle((x) => 
     { 
      Console.WriteLine("Exception: " + x.ToString()); 
     }); 
    } 
} 
+0

Это означает, что главный поток должен завершиться, чтобы проверить исключения, в то время как я хочу прекратить любую нить при исключении внутренней нити – eugeneK

+0

@eugeneK: Я отредактировал свой ответ, чтобы остановить повторение в случае внутреннего исключения. Это может быть дополнительно улучшено, чтобы остановить основной поток еще до того, как задержка в 1000 миллисекунд затянется. – Douglas

+0

Я не могу изменить мастер-петлю, хотя, – eugeneK

0

Попытка перехватить внутреннее исключение и проверить его значение в основном цикле, как если бы запрос на аннулирование:

static void Main() 
{ 
    Console.WriteLine("start"); 

    try 
    { 
     AggregateException innerException = null; 

     Task.Factory.StartNew(PrintTime, CancellationToken.None) 
        .ContinueWith(t => innerException = t.Exception, TaskContinuationOptions.OnlyOnFaulted); 

     for (int i = 0; i < 20; i++) 
     { 
      if (innerException != null) 
       throw innerException; 

      Console.WriteLine("master thread i={0}", i + 1); 
      Thread.Sleep(1000); 
     } 
    } 
    catch (AggregateException) 
    { 
     Console.WriteLine("Inner thread caused exception. Main thread handles that exception."); 
    } 
} 
+0

Я не могу сделать это в главном цикле ... – eugeneK

0

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

private static AggregateException exception = null; 

    static void Main(string[] args) 
    { 
     Console.WriteLine("Start"); 

     Task.Factory.StartNew(PrintTime, CancellationToken.None).ContinueWith(HandleException, TaskContinuationOptions.OnlyOnFaulted); 

     for (int i = 0; i < 20; i++) 
     { 
      Console.WriteLine("Master Thread i={0}", i + 1); 
      Thread.Sleep(1000); 
      if (exception != null) 
      { 
       break; 
      } 
     } 

     Console.WriteLine("Finish"); 
     Console.ReadLine(); 
    } 

    private static void HandleException(Task task) 
    { 
     exception = task.Exception; 
     Console.WriteLine(exception); 
    } 

    private static void PrintTime() 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      Console.WriteLine("Inner Thread i={0}", i + 1); 
      Thread.Sleep(1000); 
     } 

     throw new Exception("exception"); 
    } 
+0

Я не могу сделать это в главном цикле – eugeneK

0

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

static void Main() 
{ 
    Console.WriteLine("start"); 

    var mainThread = new Thread(MainThread); 
    mainThread.Start(); 

    var task = Task.Factory 
        .StartNew(PrintTime) 
        .ContinueWith(t => { Console.WriteLine("Inner thread caused exception. Main thread is being aborted."); mainThread.Abort(); }, 
           TaskContinuationOptions.OnlyOnFaulted); 
    task.Wait(); 

    Console.WriteLine("Waiting for main thread to abort..."); 
    mainThread.Join(); 
    Console.WriteLine("Main thread aborted."); 

    Console.ReadLine(); 
} 

private static void MainThread() 
{ 
    for (int i = 0; i < 20; i++) 
    { 
     Console.WriteLine("master thread i={0}", i + 1); 
     Thread.Sleep(1000); 
    } 
} 

private static void PrintTime() 
{ 
    for (int i = 0; i < 10; i++) 
    { 
     Console.WriteLine("inner thread i={0}", i + 1); 
     Thread.Sleep(1000); 
    } 

    throw new Exception("exception"); 
} 
+0

Если основной поток вылетает из-за исключения, не было ли у меня необработанное исключение с вашим кодом? – eugeneK

+0

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

+0

Извините, что виноват, будет проверять его как можно скорее – eugeneK

Смежные вопросы