PrevTask.Wait () é recomendado para ser usado com ContinueWith (da biblioteca de tarefas)?


88

Disseram-me recentemente que a forma como eu estava usando meu .ContinueWith for Tasks não era a maneira correta de usá-los. Ainda não encontrei evidências disso na internet, então vou perguntar a vocês e ver qual é a resposta. Aqui está um exemplo de como eu 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");
    });
}

Agora eu sei que este é um exemplo simples e será executado muito rápido, mas apenas suponha que cada tarefa execute uma operação mais longa. Então, o que me disseram é que no .ContinueWith, você precisa dizer prevTask.Wait (); caso contrário, você pode trabalhar antes que a tarefa anterior termine. É mesmo possível? Presumi que minha segunda e terceira tarefas só seriam executadas quando a tarefa anterior terminasse.

O que me disseram como escrever o 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");
    });
}

Respostas:


115

Ehhh .... Acho que algumas das respostas atuais estão faltando alguma coisa: o que acontece com as exceções?

A única razão pela qual você chamaria Waituma continuação seria observar uma exceção potencial do antecedente na própria continuação. A mesma observação aconteceria se você acessasse Resultno caso de a Task<T>e também se acessasse manualmente a Exceptionpropriedade. Francamente, eu não chamaria Waitou acessaria Resultporque, se houver uma exceção, você pagará o preço de aumentá-la novamente, o que é um overhead desnecessário. Em vez disso, você pode apenas verificar a IsFaultedpropriedade do antecedente Task. Alternativamente, você pode criar fluxos de trabalho bifurcados encadeando em várias continuações de irmãos que só disparam com base no sucesso ou falha com TaskContinuationOptions.OnlyOnRanToCompletione TaskContinuationOptions.OnlyOnFaulted.

Agora, não é necessário observar a exceção do antecedente na continuação, mas você pode não querer que seu fluxo de trabalho avance se, digamos, "Etapa 1" falhou. Nesse caso: a especificação TaskContinuationOptions.NotOnFaultedde suas ContinueWithchamadas evitaria que a lógica de continuação até mesmo fosse acionada.

Lembre-se de que, se suas próprias continuações não observarem a exceção, a pessoa que está aguardando a conclusão desse fluxo de trabalho geral será aquela que observará. Ou eles estão Waitsubindo o Taskrio ou aderiram à sua própria continuação para saber quando ela está completa. Se for o último, sua continuação precisaria usar a lógica de observação mencionada.


2
Por fim, alguém deu uma resposta correta. @ Travyguy9 Por favor, leia esta resposta @DrewMarsh e leia mais sobreTaskContinuationOptions
Jasper

2
Ótima resposta, eu estava procurando por "Lembre-se de que, se suas próprias continuações não observarem a exceção, a pessoa que está aguardando a conclusão desse fluxo de trabalho geral será a única a observá-la." Uma pergunta, porém, quando sua tarefa não é esperada, quem é o garçom padrão? (Não foi possível encontrar a resposta para isso)
Thibault D.

20

Você está usando corretamente.

Cria uma continuação que é executada de forma assíncrona quando a Tarefa de destino é concluída.

Fonte: método Task.ContinueWith (ação como MSDN)

Ter que chamar prevTask.Wait()todas as Task.ContinueWithinvocações parece uma maneira estranha de repetir a lógica desnecessária - isto é, fazer algo para ter "certeza absoluta" porque você na verdade não entende o que um determinado código faz. Como verificar se há um nulo apenas para lançar um ArgumentNullExceptiononde teria sido lançado de qualquer maneira.

Então, não, quem te disse isso está errado e provavelmente não entende porque Task.ContinueWithexiste.


16

Quem te disse isso?

Citando MSDN :

Cria uma continuação que é executada de forma assíncrona quando a Tarefa de destino é concluída.

Além disso, qual seria o propósito de Continue With se não estivesse esperando a conclusão da tarefa anterior?

Você pode até mesmo testá-lo:

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

Do MSDN em dianteTask.Continuewith

A tarefa retornada não será agendada para execução até que a tarefa atual seja concluída. Se os critérios especificados por meio do parâmetro continuationOptions não forem atendidos, a tarefa de continuação será cancelada em vez de agendada.

Eu acho que a maneira que você espera que funcione no primeiro exemplo é a maneira correta.



0

Ao acessar, Task.Resultvocê está fazendo uma lógica semelhante atask.wait


Sim. Podemos evitar o método Wait (). Mas funciona apenas com tarefas de resultado, por exemplo, Tarefa <bool>
Alexander Ulmaskulov

Não votado porque não aborda a questão real. Acrescenta algum valor, mas deve ser um comentário.
Sinestésico 02 de

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.