¿Se recomienda utilizar prevTask.Wait () con ContinueWith (de la biblioteca de Tareas)?


88

Así que recientemente me dijeron que cómo estaba usando mi .ContinueWith for Tasks no era la forma correcta de usarlos. Todavía tengo que encontrar evidencia de esto en Internet, así que les preguntaré y veré cuál es la respuesta. Aquí hay un ejemplo de cómo uso .ContinueWith:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
}

Ahora sé que este es un ejemplo simple y se ejecutará muy rápido, pero asuma que cada tarea realiza una operación más larga. Entonces, lo que me dijeron es que en .ContinueWith, debes decir prevTask.Wait (); de lo contrario, podría trabajar antes de que finalice la tarea anterior. ¿Es eso siquiera posible? Asumí que mi segunda y tercera tarea solo se ejecutarían una vez que finalizara su tarea anterior.

Lo que me dijeron cómo escribir el código:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 3");
    });
}

Respuestas:


115

Ehhh .... Creo que a algunas de las respuestas actuales les falta algo: ¿qué pasa con las excepciones?

La única razón por la que llamaría Waituna continuación sería observar una posible excepción del antecedente en la continuación misma. La misma observación sucedería si accediera Resulten el caso de a Task<T>y también si accediera manualmente a la Exceptionpropiedad. Francamente, no llamaría Waitni accedería Resultporque si hay una excepción, pagará el precio de volver a aumentarla, lo que es una sobrecarga innecesaria. En su lugar, puede simplemente verificar la IsFaultedpropiedad del antecedente Task. Alternativamente, puede crear flujos de trabajo bifurcados mediante el encadenamiento de varias continuaciones de hermanos que solo se activan en función del éxito o el fracaso con TaskContinuationOptions.OnlyOnRanToCompletiony TaskContinuationOptions.OnlyOnFaulted.

Ahora bien, no es necesario observar la excepción del antecedente en la continuación, pero es posible que no desee que su flujo de trabajo avance si, por ejemplo, el "Paso 1" falló. En ese caso: especificar TaskContinuationOptions.NotOnFaultedsus ContinueWithllamadas evitaría que la lógica de continuación se dispare.

Tenga en cuenta que, si sus propias continuaciones no observan la excepción, la persona que está esperando que se complete este flujo de trabajo general será quien lo observe. O están Waitavanzando Taskrío arriba o han virado en su propia continuación para saber cuándo está completo. Si es lo último, su continuación necesitaría utilizar la lógica de observación antes mencionada.


2
Por fin alguien dio una respuesta correcta. @ Travyguy9 Por favor, lea esta respuesta de @DrewMarsh y lea más sobreTaskContinuationOptions
Jasper

2
Excelente respuesta, estaba buscando "Tenga en cuenta que, si sus propias continuaciones no observan la excepción, la persona que está esperando que se complete este flujo de trabajo general será la que lo observe". Sin embargo, una pregunta, cuando no se espera su tarea, ¿quién es el camarero predeterminado? (No se pudo encontrar la respuesta a esto)
Thibault D.

20

Lo está utilizando correctamente.

Crea una continuación que se ejecuta de forma asincrónica cuando se completa la tarea de destino.

Fuente: Método Task.ContinueWith (Acción como MSDN)

Tener que llamar prevTask.Wait()en cada Task.ContinueWithinvocación parece una forma extraña de repetir lógica innecesaria, es decir, hacer algo para estar "súper seguro" porque en realidad no comprende lo que hace un cierto fragmento de código. Como buscar un nulo solo para lanzar un lugar ArgumentNullExceptiondonde se hubiera arrojado de todos modos.

Entonces, no, quien te dijo eso está equivocado y probablemente no entienda por qué Task.ContinueWithexiste.


16

¿Quién te dijo eso?

Citando MSDN :

Crea una continuación que se ejecuta de forma asincrónica cuando se completa la tarea de destino.

Además, ¿cuál sería el propósito de Continuar con si no estuviera esperando a que se completara la tarea anterior?

Incluso puedes probarlo tú mismo:

Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
        Thread.Sleep(2000);
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("I waited step 1 to be completed!");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });

5

Desde MSDN enTask.Continuewith

La tarea devuelta no se programará para su ejecución hasta que se complete la tarea actual. Si no se cumplen los criterios especificados mediante el parámetro continuationOptions, la tarea de continuación se cancelará en lugar de programarse.

Creo que la forma en que espera que funcione en el primer ejemplo es la forma correcta.



0

Al acceder Task.Result, en realidad está haciendo una lógica similar atask.wait


Si. Podemos evitar el método Wait (). Pero solo funciona con tareas de resultado, por ejemplo, Task <bool>
Alexander Ulmaskulov

Votó en contra porque no aborda la pregunta real. Agrega algo de valor, pero debería ser un comentario.
Sinaesthetic

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.