Y a-t-il un scénario où une méthode d'écriture comme celle-ci:
public async Task<SomeResult> DoSomethingAsync()
{
// Some synchronous code might or might not be here... //
return await DoAnotherThingAsync();
}
au lieu de cela:
public Task<SomeResult> DoSomethingAsync()
{
// Some synchronous code might or might not be here... //
return DoAnotherThingAsync();
}
aurait du sens?
Pourquoi utiliser la return await
construction alors que vous pouvez directement revenir Task<T>
de l' DoAnotherThingAsync()
invocation intérieure ?
Je vois du code avec return await
tant d'endroits, je pense que j'ai peut-être raté quelque chose. Mais pour autant que je sache, ne pas utiliser de mots clés async / wait dans ce cas et renvoyer directement la tâche serait fonctionnellement équivalent. Pourquoi ajouter des frais généraux supplémentaires de await
couche supplémentaire ?
Réponses:
Il y a un cas sournois quand
return
dans la méthode normale etreturn await
dans laasync
méthode se comportent différemment: lorsqu'ils sont combinés avecusing
(ou, plus généralement, toutreturn await
dans untry
bloc).Considérez ces deux versions d'une méthode:
La première méthode sera
Dispose()
l'Foo
objet dès leDoAnotherThingAsync()
retour de la méthode, ce qui est probablement bien avant qu'elle ne se termine réellement. Cela signifie que la première version est probablement boguée (carFoo
supprimée trop tôt), tandis que la deuxième version fonctionnera correctement.la source
foo.DoAnotherThingAsync().ContinueWith(_ => foo.Dispose());
Dispose()
revientvoid
. Vous auriez besoin de quelque chose commereturn foo.DoAnotherThingAsync().ContinueWith(t -> { foo.Dispose(); return t.Result; });
. Mais je ne sais pas pourquoi feriez-vous cela lorsque vous pouvez utiliser la deuxième option.{ var task = DoAnotherThingAsync(); task.ContinueWith(_ => foo.Dispose()); return task; }
. Le cas d'utilisation est assez simple: si vous êtes sur .NET 4.0 (comme la plupart), vous pouvez toujours écrire du code asynchrone de cette façon qui fonctionnera bien à partir des applications 4.5.Foo
qu'après la fin du retourTask
, ce que je n'aime pas, car il introduit inutilement la concurrence.Si vous n'en avez pas besoin
async
(c.-à-d., Vous pouvez retourner leTask
directement), alors ne l'utilisez pasasync
.Il y a certaines situations où cela
return await
est utile, comme si vous avez deux opérations asynchrones à faire:Pour plus d'informations sur les
async
performances, consultez l' article et la vidéo MSDN de Stephen Toub sur le sujet.Mise à jour: j'ai écrit un article de blog qui va beaucoup plus en détail.
la source
await
est utile dans le deuxième cas? Pourquoi pasreturn SecondAwait(intermediate);
?return SecondAwait(intermediate);
l'objectif dans ce cas? Je pense quereturn await
c'est redondant ici aussi ...await
dans la première ligne, vous devez également l'utiliser dans la seconde.async
version utilisera moins de ressources (threads) que votre version synchrone.SecondAwait
est `chaîne, le message d'erreur est:" CS4016: Puisqu'il s'agit d'une méthode asynchrone, l'expression de retour doit être de type 'chaîne' plutôt que 'Tâche <chaîne>' ".La seule raison pour laquelle vous voudriez le faire est s'il y en a un autre
await
dans le code précédent, ou si vous manipulez le résultat d'une manière ou d'une autre avant de le retourner. Une autre façon dont cela pourrait se produire est detry/catch
modifier la façon dont les exceptions sont gérées. Si vous ne faites rien de tout cela, vous avez raison, il n'y a aucune raison d'ajouter la surcharge de la méthodeasync
.la source
return await
nécessaire (au lieu de simplement renvoyer la tâche d'invocation d'enfant) même s'il y en a d'autres en attente dans le code précédent . Pourriez-vous s'il vous plaît fournir des explications?async
comment attendriez-vous la première tâche? Vous devez marquer la méthode commeasync
si vous souhaitez utiliser des attentes. Si la méthode est marquée commeasync
et que vous avez unawait
code antérieur, vous devez effectuerawait
la deuxième opération asynchrone pour qu'elle soit du type approprié. Si vous venez de le supprimer,await
il ne se compilera pas car la valeur de retour ne sera pas du type approprié. Puisque la méthode estasync
le résultat est toujours enveloppé dans une tâche.async
méthode ne renvoie pas de tâche, vous retournez le résultat de la tâche qui sera ensuite encapsulé.Task<Type>
explicitement, tandis queasync
dicte de retournerType
(ce que le compilateur lui-même transformeraitTask<Type>
).async
c'est juste du sucre syntaxique pour câbler explicitement les suites. Vous ne devezasync
rien faire, mais quand vous livrez à une opération asynchrone non triviale , il est considérablement plus facile de travailler avec. Par exemple, le code que vous avez fourni ne propage pas réellement les erreurs comme vous le souhaitez, et le faire correctement dans des situations encore plus complexes commence à devenir beaucoup plus difficile. Bien que vous n'en ayez jamais besoinasync
, les situations que je décris sont celles où il ajoute de la valeur pour l'utiliser.Un autre cas dont vous devrez peut-être attendre le résultat est celui-ci:
Dans ce cas,
GetIFooAsync()
doit attendre le résultatGetFooAsync
car le type deT
est différent entre les deux méthodes etTask<Foo>
n'est pas directement attribuable àTask<IFoo>
. Mais si vous attendez le résultat, il devient tout simplement ceFoo
qui est directement attribuable àIFoo
. Ensuite, la méthode async reconditionne simplement le résultat à l'intérieurTask<IFoo>
et à l'extérieur.la source
Task<>
est invariante.Rendre la méthode "thunk" autrement simple asynchrone crée une machine d'état asynchrone en mémoire alors que celle non-async ne le fait pas. Bien que cela puisse souvent inciter les gens à utiliser la version non asynchrone car elle est plus efficace (ce qui est vrai), cela signifie également qu'en cas de blocage, vous n'avez aucune preuve que cette méthode est impliquée dans la "pile de retour / continuation". ce qui rend parfois plus difficile la compréhension du blocage.
Alors oui, lorsque la perf n'est pas critique (et ce n'est généralement pas le cas), je lancerai asynchrone sur toutes ces méthodes de thunk afin d'avoir la machine d'état asynchrone pour m'aider à diagnostiquer les blocages plus tard, et aussi pour m'assurer que si ces les méthodes de thunk évoluent au fil du temps, elles seront sûres de retourner les tâches en défaut au lieu de lancer.
la source
Si vous n'utilisez pas return wait, vous pouvez ruiner la trace de votre pile pendant le débogage ou lorsqu'elle est imprimée dans les journaux des exceptions.
Lorsque vous renvoyez la tâche, la méthode a rempli son objectif et elle est hors de la pile d'appels. Lorsque vous utilisez,
return await
vous le laissez dans la pile des appels.Par exemple:
Pile d'appels lors de l'utilisation de l'attente: A en attente de la tâche de B => B en attente de la tâche de C
Pile d'appels lorsque vous n'utilisez pas wait: A attend la tâche de C, que B a renvoyée.
la source
Cela m'embrouille également et je pense que les réponses précédentes ont ignoré votre question réelle:
Eh bien, parfois, vous voulez vraiment un
Task<SomeType>
, mais la plupart du temps, vous voulez en fait une instanceSomeType
, c'est-à-dire le résultat de la tâche.Depuis votre code:
Une personne qui ne connaît pas la syntaxe (moi, par exemple) pourrait penser que cette méthode devrait retourner un
Task<SomeResult>
, mais comme elle est marquée avecasync
, cela signifie que son type de retour réel estSomeResult
. Si vous utilisez simplementreturn foo.DoAnotherThingAsync()
, vous retourneriez une tâche qui ne serait pas compilée. La bonne façon est de renvoyer le résultat de la tâche, donc lereturn await
.la source
var task = DoSomethingAsync();
cela vous donnerait une tâche, pasT
async/await
chose. À ma connaissanceTask task = DoSomethingAsync()
, alors que lesSomething something = await DoSomethingAsync()
deux fonctionnent. Le premier vous donne la tâche appropriée, tandis que le second, en raison duawait
mot - clé, vous donne le résultat de la tâche une fois terminée. Je pourrais, par exemple, avoirTask task = DoSomethingAsync(); Something something = await task;
.