Créer une tâche terminée

197

Je veux créer un Task(pas Task<T>) terminé . Y a-t-il quelque chose de intégré à .NET pour ce faire?

Une question connexe: créer une tâche terminée <T>

Timothy Shields
la source
2
It seems like the answer I'm getting from everyone is that using a garbage value like this is the correct way. That there isn't a way to do this without the garbage value is disappointing -- oh well.Quels problèmes pensez-vous que cela a? Si vous en cachez un seul, Taskvotre programme entier prend un bit de mémoire supplémentaire. Ce n'est rien . De plus, on pourrait créer une tâche terminée sans le faire, ce ne serait pas mieux.
Servy
10
Oh ma déception n'a rien à voir avec le fait d'avoir à utiliser de la mémoire supplémentaire. C'est juste que les valeurs de déchets n'importe où dans le code ne sont pas élégantes.
Timothy Shields
1
Notez qu'aujourd'hui, il y a ValueTaskdes tâches terminées (c'est-à-dire pour les valeurs que vous avez déjà afin que le code soit essentiellement synchrone), ce qui vous fera économiser une allocation.
nawfal

Réponses:

247

La dernière version de .Net (v4.6) ajoute juste cela, une tâche intégrée .

Task completedTask = Task.CompletedTask;

Cette propriété est implémentée comme un singleton sans verrouillage, vous utiliserez donc presque toujours la même tâche terminée.

i3arnon
la source
1
Je viens de vérifier le dernier VS 14 CTP et de créer un projet 4.5.3, Task.CompletedTaskest toujours interne.
Peter Ritchie
1
2. Soit vous n'avez pas téléchargé le dernier CTP (qui est 4 et lié à partir de ce site) ou vous n'avez pas spécifié la version 4.5.3. Voici ce qu'il y a sur ma machine . @PeterRitchie
i3arnon
J'ai créé une machine virtuelle à partir de l'image Visual Studio 14 sur Azure.
Peter Ritchie
1
@PeterRitchie À partir du modèle Azure VS14CTP4
i3arnon
Je travaille sur Mac OS X avec mono 5.4.1.7 et j'obtiens la même erreur. Comment puis-je réparer cela?
Khaled Annajar
153

Task<T>est implicitement convertible en Task, donc obtenez simplement un Task<T>(avec n'importe Tquelle valeur) et utilisez-le. Vous pouvez utiliser quelque chose comme ça pour cacher le fait qu'un résultat réel est là, quelque part.

private static Task completedTask = Task.FromResult(false);
public static Task CompletedTask()
{
    return completedTask;
}

Notez que puisque nous n'exposons pas le résultat et que la tâche est toujours terminée, nous pouvons mettre en cache une seule tâche et la réutiliser.

Si vous utilisez .NET 4.0 et n'en avez pas, FromResultvous pouvez créer le vôtre en utilisant TaskCompletionSource:

public static Task<T> FromResult<T>(T value)
{
    var tcs = new TaskCompletionSource<T>();
    tcs.SetResult(value);
    return tcs.Task;
}
Servy
la source
8
Si vous utilisez 4.0, vous pouvez ajouter la bibliothèque Microsoft.Bcl.Async pour obtenir TaskEx.FromResult, ainsi que d'autres choses pratiques comme WhenAll
Carl
@Servy Qu'est-ce que cela change de FromResult (false) et FromResult (true)?
Francesco Bonizzi
3
@FrancescoB. Si vous regardez le résultat, cela change la valeur booléenne du résultat. Si vous ignorez le résultat, le résultat n'est pas pertinent.
Servy
66

Ma méthode préférée pour ce faire est d'appeler Task.WhenAll()sans argument. La documentation MSDN indique que "Si le tableau / énumérable fourni ne contient aucune tâche, la tâche renvoyée passera immédiatement à un état RanToCompletion avant d'être renvoyée à l'appelant.". Cela ressemble à ce que vous voulez.

Mise à jour: j'ai trouvé la source sur la source de référence de Microsoft ; là, vous pouvez voir que Task.WhenAll contient les éléments suivants:

return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
            Task.CompletedTask :
            new WhenAllPromise(tasks);

Donc Task.CompletedTask est en effet interne, mais il est exposé en appelant WhenAll () sans arguments.

Richiban
la source
9
bien que cela semble un peu hacky, il a le soutien des docs, donc je ne sais pas, j'aime bien!
sara
2
Pour moi et mon code contraint .NET 4.5.2, c'est assez élégant. Merci!
Stas Ivanov
36

J'utiliserais Task.Delay(0). En interne, il renvoie une instance mise en cache d'un terminé Task<T>. C'est exactement ce que la réponse actuelle suggère de faire de toute façon, seulement maintenant vous n'avez pas à mettre en cache une instance vous-même, et vous n'avez pas non plus de valeurs inutiles dans votre code.

Vous pensez peut-être que vous pouvez utiliser à la Task.Yield()place, mais il s'avère que le résultat de Task.Yield()n'est pas un sous-type de Task, tandis que le résultat de Task.Delay(0)est. C'est l'une des subtiles différences entre les deux.

gzak
la source
30

Vous pouvez utiliser Task.FromResult (dans .NET 4.5) pour renvoyer une requête terminée Task<T>.

Si vous avez besoin d'un non générique Task, vous pouvez toujours utiliser Task.FromResult(0)ou similaire, car il Task<T>s'agit d'une sous-classe de Task.

Reed Copsey
la source
11

Pour .Net 4.6 et supérieur, utilisez

return Task.CompletedTask;

Pour la version inférieure, vous pouvez utiliser

return new Task(() => { });
Icen
la source
3
Pour l'approche pré 4.6, attention! Vous devez démarrer la tâche ou elle ne se terminera jamais! Que diriez-vous d'utiliser à la return Task.Delay(0);place?
Miquel
0

Que diriez-vous:

#pragma warning disable 1998
    public async Task emptyTask() {
    }
#pragma warning restore 1998

Vous pouvez ignorer la suppression des avertissements si cela ne vous dérange pas.

Dorus
la source
Je crois que le marquage d'une méthode asynccrée toujours l'appareil de la machine d'état entière même si vous ne l'utilisez pas, donc retourner une tâche vide est plus efficace pour les scénarios à faibles ressources.
Calvin Fisher