Pergunta

eu estava lendo A natureza da TaskCompletionFonte, uma postagem de Stephen Toub.

public static Task RunAsync(Action action)
{
    var tcs = new TaskCompletionSource<Object>();
    ThreadPool.QueueUserWorkItem(_ =>
    {
        try
        {
            action();
            tcs.SetResult(null);
        }
        catch(Exception exc) { tcs.SetException(exc); }
    });
    return tcs.Task;
}

Já que não nos importamos mais com o tipo de T é, eu optei por usar Object.Então, quando o Action é executado com sucesso, SetResult ainda é usado para fazer a transição Task no RanToCompletion estado final;no entanto, uma vez que o valor real do resultado é irrelevante, null é usado. Finalmente, RunAsync retorna Task em vez de Task<Object>.Claro, o instanciado tasko tipo de ainda é Task<Object>, mas não precisamos nos referir a ele como tal, e o consumidor desse método não precisa se preocupar com esses detalhes de implementação.

Eu particularmente não entendo por que o método deveria retornar Task em vez de Task<object> (é por isso que enfatizei a frase em negrito).Eu sei que o método está configurado para retornar Task mas tcs é um TaskCompletionSource<Object>, não TaskCompletionSource (o que está errado, eu acho).

Foi útil?

Solução

Não existe um não genérico TaskCompletionSource e considerando que tudo que você quer é uma tarefa sem resultado, o resultado não importa.O chamador não sabe e não se importa, neste caso, que a Tarefa é na verdade uma Task<object>, O chamador apenas awaité isso e obtém uma exceção, se houver.O chamador não tem conhecimento do resultado real.

É claro que isso é facilitado pelo fato de que Task<T> herda de Task


Também é comum encontrar um Task<bool> que retorna falso, ou Task<int> com 0.

Outras dicas

Não há não genérico TaskCompletionSource classe para criar instâncias de Task que não são casos de Task<T>.Isso deixa duas opções para o parâmetro de tipo genérico para TaskCompletionSource<T> quando você não se importa (ou não fornece) o valor de retorno:

  1. Use um tipo existente arbitrário, como object, como o tipo de retorno.Defina o valor como null para indicar a conclusão da tarefa.
  2. Use um tipo não público específico e defina o valor como null para indicar a conclusão da tarefa.

Quando eu crio um TaskCompletionSource<T> por exemplo, com a finalidade de fornecer um Task sem valor de retorno, prefiro usar um tipo não público dedicado para garantir que o código consumido não confunda o retornado Task como uma instância de Task<T> onde o resultado tem significado.

Primeiro, defino a seguinte classe (pode ser um private sealed class se estiver aninhado em outro tipo):

internal sealed class VoidResult
{
}

Então, em vez de usar TaskCompletionSource<object> para a fonte de conclusão, eu uso TaskCompletionSource<VoidResult>.Desde o VoidResult tipo não é acessível chamando o código, o usuário não poderá converter o Task opor-se a uma instância de Task<VoidResult>.

Eu particularmente não entendo por que o método deveria retornar Task em vez de Task<object>

Porque quando você voltar Task<Object> isso significa que quando este método for concluído, ele produzirá algum valor útil do tipo Object.neste caso não estamos produzindo nenhum resultado, é por isso que Stephen optou por retornar Task.

Se estamos lidando com Func<Object> então voltando Task<Object> seria apropriado, como Func produzirá algum resultado, podemos optar por devolvê-lo.

Por que TaskCompletionSource<Object>, não TaskCompletionSource?

Porque não existe tal coisa.Não há nenhum genérico TaskCompletionSource.

Se você devolveu um Task<object>, então var result = await RunAsync(...) sempre voltaria null, já que é para isso que você está definindo o resultado.

O cliente não se importa com isso, então você apenas retorna um Task.

O ideal é que você use um TaskCompletionSource internamente, em vez de um TaskCompletionSource<object>, e apenas chame algo como SetCompleted() em vez de SetResult(null).Mas esse tipo não existe.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top