Question

je lisais La nature de TaskCompletionSource, un article 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;
}

Puisque nous ne nous soucions plus du type de T c'est-à-dire que j'ai utilisé par défaut Object.Puis, lorsque le Action est exécuté avec succès, SetResult est toujours utilisé pour faire la transition Task dans le RanToCompletion état final ;cependant, puisque la valeur réelle du résultat n'est pas pertinente, null est utilisé. Enfin, RunAsync Retour Task plutôt que Task<Object>.Bien sûr, l'instancié taskLe type est toujours Task<Object>, mais nous n'avons pas besoin de nous y référer en tant que tel, et le consommateur de cette méthode n'a pas besoin de se soucier de ces détails d'implémentation.

Je ne comprends pas particulièrement pourquoi la méthode devrait revenir Task plutôt que Task<object> (c'est pourquoi j'ai souligné la phrase en gras).Je sais que la méthode est prête à revenir Task mais tcs est un TaskCompletionSource<Object>, pas TaskCompletionSource (ce qui est faux, je pense).

Était-ce utile?

La solution

Il n'y a pas de non générique TaskCompletionSource et étant donné que tout ce que vous voulez, c'est une tâche sans résultat, le résultat n'a pas d'importance.L'appelant ne sait pas et ne s'en soucie pas dans ce cas, que la tâche est en réalité une Task<object>, L'appelant vient de awaitc'est tout, et obtient une exception s'il y en a une.L'appelant n'est pas au courant du résultat réel.

Ceci est évidemment facilité par le fait que Task<T> hérite de Task


Il est également courant de trouver un Task<bool> qui renvoie faux, ou Task<int> avec 0.

Autres conseils

Il n'y a pas de non-générique TaskCompletionSource classe pour créer des instances de Task qui ne sont pas des cas de Task<T>.Cela laisse deux options pour le paramètre de type générique pour TaskCompletionSource<T> lorsque vous ne vous souciez pas (ou ne fournissez pas) la valeur de retour :

  1. Utilisez un type existant arbitraire, tel que object, comme type de retour.Définissez la valeur sur null pour indiquer l'achèvement de la tâche.
  2. Utilisez un type non public spécifique et définissez la valeur sur null pour indiquer l'achèvement de la tâche.

Quand je crée un TaskCompletionSource<T> par exemple dans le but de fournir un Task sans valeur de retour, je préfère utiliser un type non public dédié pour garantir que le code consommé ne confondra pas le retour Task comme exemple de Task<T> où le résultat a un sens.

Tout d'abord, je définis la classe suivante (cela peut être un private sealed class s'il est imbriqué dans un autre type :

internal sealed class VoidResult
{
}

Ensuite, au lieu d'utiliser TaskCompletionSource<object> pour la source de complétion, j'utilise TaskCompletionSource<VoidResult>.Depuis le VoidResult type n'est pas accessible en appelant le code, l'utilisateur ne pourra pas diffuser le Task s'opposer à un cas de Task<VoidResult>.

Je ne comprends pas particulièrement pourquoi la méthode devrait revenir Task plutôt que Task<object>

Parce que quand tu reviens Task<Object> cela signifie que lorsque cette méthode sera terminée, elle produira une valeur utile de type Object.dans ce cas, nous ne produisons aucun résultat, c'est pourquoi Stephen a choisi de revenir Task.

Si nous avons affaire à Func<Object> puis je reviens Task<Object> serait approprié, comme Func produira un résultat, nous pouvons choisir de le renvoyer.

Pourquoi TaskCompletionSource<Object>, pas TaskCompletionSource?

Parce qu'il n'y a rien de tel.Il n'y a pas de non générique TaskCompletionSource.

Si vous avez retourné un Task<object>, alors var result = await RunAsync(...) je reviendrais toujours null, puisque c'est ce sur quoi vous définissez le résultat.

Le client ne s'en soucie pas, alors vous renvoyez simplement un Task.

Idéalement, vous utiliseriez un TaskCompletionSource en interne, au lieu d'un TaskCompletionSource<object>, et appelle simplement quelque chose comme SetCompleted() au lieu de SetResult(null).Mais ce type n’existe pas.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top