Vous avez posé plusieurs questions dans votre question. Je vais les décomposer légèrement différemment de vous. Mais permettez-moi d'abord de répondre directement à la question.
Nous voulons tous un appareil photo léger, de haute qualité et bon marché, mais comme le dit le proverbe, vous ne pouvez en obtenir que deux au maximum. Vous êtes dans la même situation ici. Vous voulez une solution qui soit efficace, sûre et partage du code entre les chemins synchrone et asynchrone. Vous n'en obtiendrez que deux.
Permettez-moi de vous expliquer pourquoi. Commençons par cette question:
J'ai vu que les gens disent que vous pouvez utiliser GetAwaiter().GetResult()
une méthode asynchrone et l'invoquer à partir de votre méthode de synchronisation? Ce fil est-il sûr dans tous les scénarios?
Le point de cette question est "puis-je partager les chemins synchrones et asynchrones en faisant simplement que le chemin synchrone fasse une attente synchrone sur la version asynchrone?"
Permettez-moi d'être très clair sur ce point car il est important:
VOUS DEVEZ IMMÉDIATEMENT ARRÊTER DE PRENDRE TOUT CONSEIL DE CES PERSONNES .
C'est un très mauvais conseil. Il est très dangereux d'extraire de façon synchrone un résultat d'une tâche asynchrone, sauf si vous avez la preuve que la tâche s'est terminée normalement ou anormalement .
La raison pour laquelle ce conseil est extrêmement mauvais est, bien, envisager ce scénario. Vous voulez tondre la pelouse, mais votre lame de tondeuse à gazon est cassée. Vous décidez de suivre ce workflow:
- Commandez une nouvelle lame sur un site Web. Il s'agit d'une opération asynchrone à latence élevée.
- Attendez de manière synchrone - c'est-à-dire, dormez jusqu'à ce que vous ayez la lame en main .
- Vérifiez régulièrement la boîte aux lettres pour voir si la lame est arrivée.
- Retirez la lame de la boîte. Vous l'avez maintenant en main.
- Installez la lame dans la tondeuse.
- Tondre la pelouse.
Ce qui se produit? Vous dormez pour toujours car l'opération de vérification du courrier est désormais déclenchée par un événement qui se produit après l'arrivée du courrier .
Il est extrêmement facile d'entrer dans cette situation lorsque vous attendez de manière synchrone une tâche arbitraire. Cette tâche peut avoir un travail planifié dans le futur du thread qui attend maintenant , et maintenant ce futur n'arrivera jamais parce que vous l'attendez.
Si vous faites une attente asynchrone, tout va bien! Vous vérifiez régulièrement le courrier, et pendant que vous attendez, vous faites un sandwich ou faites vos impôts ou autre chose; vous continuez à travailler pendant que vous attendez.
N'attendez jamais de façon synchrone. Si la tâche est terminée, elle n'est pas nécessaire . Si la tâche n'est pas terminée mais planifiée pour s'exécuter à partir du thread en cours, elle est inefficace car le thread en cours pourrait servir d'autres travaux au lieu d'attendre. Si la tâche n'est pas terminée et que l'exécution est planifiée sur le thread en cours, elle est suspendue pour attendre de manière synchrone. Il n'y a aucune bonne raison d'attendre de manière synchrone, à nouveau, sauf si vous savez déjà que la tâche est terminée .
Pour plus d'informations sur ce sujet, voir
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Stephen explique le scénario du monde réel beaucoup mieux que moi.
Considérons maintenant "l'autre direction". Pouvons-nous partager du code en faisant simplement que la version asynchrone fasse la version synchrone sur un thread de travail?
C'est peut - être et probablement probablement une mauvaise idée, pour les raisons suivantes.
Il est inefficace si le fonctionnement synchrone est un travail d'E / S à latence élevée. Cela engage essentiellement un travailleur et le fait dormir jusqu'à ce qu'une tâche soit terminée. Les fils sont incroyablement chers . Ils consomment un million d'octets d'espace d'adressage minimum par défaut, ils prennent du temps, ils prennent les ressources du système d'exploitation; vous ne voulez pas graver un fil faisant un travail inutile.
L'opération synchrone peut ne pas être écrite pour être thread-safe.
C'est une technique plus raisonnable si le travail à latence élevée est liée processeur, mais si elle est alors vous ne voulez probablement pas remettre tout simplement hors d'un thread de travail. Vous souhaitez probablement utiliser la bibliothèque parallèle de tâches pour la paralléliser à autant de processeurs que possible, vous voulez probablement une logique d'annulation et vous ne pouvez pas simplement faire faire la version synchrone, car alors ce serait déjà la version asynchrone .
Lectures complémentaires; encore une fois, Stephen l'explique très clairement:
Pourquoi ne pas utiliser Task.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
Plus de scénarios "faire et ne pas faire" pour Task.Run:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
Qu'est-ce que cela nous laisse alors? Les deux techniques de partage de code conduisent à des blocages ou à de grandes inefficacités. La conclusion à laquelle nous arrivons est que vous devez faire un choix. Voulez-vous un programme qui est efficace et correct et qui ravit l'appelant, ou voulez-vous enregistrer quelques frappes de touches entraînées par la duplication d'une petite quantité de code entre les chemins synchrone et asynchrone? Vous n'obtenez pas les deux, j'ai peur.