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");
});
}
c#
task-parallel-library
Travyguy9
fuente
fuente
Respuestas:
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
Wait
una continuación sería observar una posible excepción del antecedente en la continuación misma. La misma observación sucedería si accedieraResult
en el caso de aTask<T>
y también si accediera manualmente a laException
propiedad. Francamente, no llamaríaWait
ni accederíaResult
porque si hay una excepción, pagará el precio de volver a aumentarla, lo que es una sobrecarga innecesaria. En su lugar, puede simplemente verificar laIsFaulted
propiedad del antecedenteTask
. 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 conTaskContinuationOptions.OnlyOnRanToCompletion
yTaskContinuationOptions.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.NotOnFaulted
susContinueWith
llamadas 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
Wait
avanzandoTask
rí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.fuente
TaskContinuationOptions
Lo está utilizando correctamente.
Fuente: Método Task.ContinueWith (Acción como MSDN)
Tener que llamar
prevTask.Wait()
en cadaTask.ContinueWith
invocació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 lugarArgumentNullException
donde se hubiera arrojado de todos modos.Entonces, no, quien te dijo eso está equivocado y probablemente no entienda por qué
Task.ContinueWith
existe.fuente
¿Quién te dijo eso?
Citando MSDN :
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"); });
fuente
Desde MSDN en
Task.Continuewith
Creo que la forma en que espera que funcione en el primer ejemplo es la forma correcta.
fuente
También es posible que desee considerar el uso de Task.Run en lugar de Task.Factory.StartNew.
La publicación del blog de Stephen Cleary y la publicación de Stephen Toub a la que hace referencia explican las diferencias. También hay una discusión en esta respuesta .
fuente
Al acceder
Task.Result
, en realidad está haciendo una lógica similar atask.wait
fuente
Reiteraré lo que muchos ya han hablado,
prevTask.Wait()
es innecesario .Para obtener más ejemplos, puede ir a Chaining Tasks using Continuation Tasks , otro enlace más de Microsoft con buenos ejemplos.
fuente