2010-07-19 12 views
8

Estoy trabajando en un Web Hook en .NET 4.0 que ejecutará una lambda de forma asíncrona y luego publicará el resultado en un URI dado cuando está terminado.Quiero que la tarea maneje las excepciones que se lanzan, pero me es difícil evitar que lleguen a la matriz

Tengo que funcionar, pero ahora quiero que la Tarea maneje las excepciones que se lanzan, y me resulta difícil evitar que lleguen a la matriz.

Aquí es parte de mi código:

private readonly Func<T> _startTask; 
private readonly string _responseUri; 

public Task<T> Begin() 
{ 
    var task = new Task<T>(_startTask); 
    task.ContinueWith<T>(End); 
    task.Start(); 
    return task; 
} 

private T End(Task<T> task) 
{ 
    if (task.IsFaulted) 
    { 
     return HandleException(task); 
    } 

    var result = task.Result; 
    WebHookResponse.Respond(result, _responseUri); 
    return result; 
} 

private T HandleException(Task<T> task) 
{ 
    WebHookResponse.HandleException(task.Exception.InnerException, _responseUri); 
    return null; 
} 

Una versión alternativa que he tratado llamadas ContinueWith() dos veces para registrar una continuidad para funcionar OnlyOnRanToCompletion y uno para funcionar OnlyOnFaulted. (No estoy seguro de si llama dos veces ContinueWith() es correcta.):

public Task<T> Begin() 
{ 
    var task = new Task<T>(_startTask); 
    task.ContinueWith<T>(End, TaskContinuationOptions.OnlyOnRanToCompletion); 
    task.ContinueWith<T>(HandleException, TaskContinuationOptions.OnlyOnFaulted); 
    task.Start(); 
    return task; 
} 

private T End(Task<T> task) 
{ 
    var result = task.Result; 
    WebHookResponse.Respond(result, _responseUri); 
    return result; 
} 

private T HandleException(Task<T> task) 
{ 
    WebHookResponse.HandleException(task.Exception.InnerException, _responseUri); 
    return null; 
} 

Así que básicamente buscan una forma para cada tarea manejar sus propias excepciones a través de una función de continuación. Tal como está, la función de continuación HandlException nunca se llama en ninguno de los ejemplos anteriores.

Estoy causando las excepciones en un caso de prueba, y debo mencionar que estoy usando una llamada Tasks.WaitAll(tasks); en una serie de tareas para asegurarme de que todas las tareas estén completas antes de hacer mis afirmaciones, y no estoy seguro si esa llamada hace una diferencia en cómo las Tareas manejan las excepciones. Actualmente, WaitAll arroja una AggregationException que agrega las excepciones para cada una de las Tareas porque no están siendo manejadas por la función de continuación HandleException.

+0

No veo el problema. ¿Qué comportamiento estás viendo? –

+0

Lo sentimos, no se está llamando a la función de continuación HandleException. Las Tasks.WaitAll (tareas) generan una AggregationException; llamada. No quiero ver una AggregationException. Acabo de actualizar la pregunta. –

+0

@Martin Owen Hola, mi HandleExceptionContinuation tampoco se llama y mi evento UnobservedTaskException tampoco se activará aquí: http://stackoverflow.com/questions/11831844/unobservedtaskexception-being-throw-but-it-is-handled-by- a-taskscheduler-unbser ¿encontraste alguna solución? – newway

Respuesta

1

Quizás, a la respuesta de Henk Holterman, el pedido hace la diferencia. Es decir,

var task = new Task<T>(_startTask); 
task = task.ContinueWith<T>(HandleException, TaskContinuationOptions.OnlyOnFaulted); 
task = task.ContinueWith<T>(End, TaskContinuationOptions.OnlyOnRanToCompletion); 
task.Start(); 

aseguraría que se ejecute HandleException cuando sea necesario.

+0

No, no es así. Ambas continuaciones se llamarán si es necesario, independientemente del orden en el que llamaste 'ContinueWith' (obviamente, solo una vez cada vez, ya que sus' TaskContinuationOptions' son mutuamente excluyentes). –

+0

Si lo hizo en ese orden, las excepciones agregadas en End no se manejarán. HandleException siempre tendría que ser el último llamado, como un sweeper en el fútbol está detrás de toda la defensa para que pueda barrer todo lo que pueda pasar, lo mismo. – Despertar

3

Una continuación de tarea que observa la excepción de la tarea no maneja la excepción. Todavía sucede dondequiera que esperas para terminar la tarea.

Dijiste que estabas llamando a WaitAll (tareas) antes de afirmar. Apuesto a que su continuación se hubiera ejecutado si le hubiese dado suficiente tiempo, pero la excepción en WaitAll() normalmente va a ocurrir antes de que se ejecute su continuación. Entonces, sus afirmaciones probablemente fallaron antes de que su continuación tuviera la oportunidad de terminar su trabajo.

1

Uso este enfoque porque proporciona un buen estilo de codificación declarativa y no ensucia el código con los aspectos de manejo de excepciones.

class Program 
{ 
    static void Main() 
    { 
     Task.Factory.StartNew(
      () => 
       { 
        throw new Exception(); 
       }) 
      .Success(() => Console.WriteLine("Works")) 
      .Fail((ex) => Console.WriteLine("Fails")).Wait(); 

     Console.ReadKey(); 
    } 
} 

public static class TaskExtensions 
{ 
    public static Task Success(this Task task, Action onSuccess) 
    { 
     task.ContinueWith((t) => 
     { 
      if (!t.IsFaulted) 
      { 
       onSuccess(); 
      } 
     }); 

     return task; 
    } 

    public static Task Fail(this Task task, Action<Exception> onFail) 
    { 
     return task.ContinueWith(
      (t) => 
      { 
       if (t.IsFaulted) 
       { 
        t.Exception.Handle(x => true); 
        onFail(t.Exception.Flatten()); 
       } 
      }); 
    } 
} 
Cuestiones relacionadas