Je suis nouveau dans la programmation asynchrone avec le async
modificateur. J'essaie de comprendre comment m'assurer que ma Main
méthode d'une application console s'exécute réellement de manière asynchrone.
class Program
{
static void Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = bs.GetList();
}
}
public class Bootstrapper {
public async Task<List<TvChannel>> GetList()
{
GetPrograms pro = new GetPrograms();
return await pro.DownloadTvChannels();
}
}
Je sais que cela ne fonctionne pas de manière asynchrone à partir du "haut". Puisqu'il n'est pas possible de spécifier le async
modificateur sur la Main
méthode, comment puis-je exécuter le code dans main
asynchrone?
c#
.net
asynchronous
console-application
danielovich
la source
la source
Réponses:
Comme vous l'avez découvert, dans VS11, le compilateur interdira une
async Main
méthode. Cela a été autorisé (mais jamais recommandé) dans VS2010 avec le CTP Async.J'ai récemment publié des articles de blog sur async / wait et les programmes de console asynchrones en particulier. Voici quelques informations de base du post d'introduction:
Voici pourquoi il s'agit d'un problème dans les programmes de console avec un
async Main
:Une solution consiste à fournir votre propre contexte - une «boucle principale» pour votre programme de console compatible asynchrone.
Si vous avez une machine avec le CTP Async, vous pouvez utiliser à
GeneralThreadAffineContext
partir de Mes Documents \ Microsoft Visual Studio CTP Async \ Samples (test C #) Unit Testing \ AsyncTestUtilities . Alternativement, vous pouvez utiliser àAsyncContext
partir de mon package Nito.AsyncEx NuGet .Voici un exemple utilisant
AsyncContext
;GeneralThreadAffineContext
a une utilisation presque identique:Alternativement, vous pouvez simplement bloquer le thread principal de la console jusqu'à ce que votre travail asynchrone soit terminé:
Notez l'utilisation de
GetAwaiter().GetResult()
; cela évite leAggregateException
wrapping qui se produit si vous utilisezWait()
ouResult
.Mise à jour, 2017-11-30: à partir de Visual Studio 2017 Update 3 (15.3), la langue prend désormais en charge un
async Main
- tant qu'elle renvoieTask
ouTask<T>
. Vous pouvez donc maintenant faire ceci:La sémantique semble être la même que le
GetAwaiter().GetResult()
style de blocage du thread principal. Cependant, il n'y a pas encore de spécification de langage pour C # 7.1, donc ce n'est qu'une hypothèse.la source
Wait
ouResult
, et il n'y a rien de mal à cela. Mais sachez qu'il existe deux différences importantes: 1) toutes lesasync
continuations s'exécutent sur le pool de threads plutôt que sur le thread principal, et 2) toutes les exceptions sont encapsulées dans unAggregateException
.<LangVersion>latest</LangVersion>
au fichier csproj, comme indiqué ici .Vous pouvez résoudre ce problème avec cette construction simple:
Cela mettra tout ce que vous faites sur le ThreadPool où vous le souhaitez (de sorte que les autres tâches que vous démarrez / attendez n'essaient pas de rejoindre un thread, elles ne devraient pas), et attendez que tout soit fait avant de fermer l'application Console. Pas besoin de boucles spéciales ou de bibliothèques externes.
Edit: Incorporer la solution d'Andrew pour les exceptions non capturées.
la source
Wait()
par,GetAwaiter().GetResult()
vous éviterez leAggregateException
wrapper lorsque les choses se lanceront.async main
est introduit dans C # 7.1, au moment de la rédaction de cet article.Vous pouvez le faire sans avoir besoin de bibliothèques externes également en procédant comme suit:
la source
getListTask.Result
s'agit également d'un appel bloquant et que le code ci-dessus pourrait être écrit sansTask.WaitAll(getListTask)
.GetList
vous lancez, vous devrez attraper unAggregateException
et interroger ses exceptions pour déterminer l'exception réelle levée. Vous pouvez, cependant, appelerGetAwaiter()
pour obtenir leTaskAwaiter
pourTask
, et faire appelGetResult()
à cela, c'est-à-direvar list = getListTask.GetAwaiter().GetResult();
. Lors de l'obtention du résultat deTaskAwaiter
(également un appel de blocage), les exceptions levées ne seront pas encapsulées dans unAggregateException
.En C # 7.1, vous pourrez faire un bon async Main . Les signatures appropriées pour la
Main
méthode ont été étendues à:Par exemple, vous pourriez faire:
Au moment de la compilation, la méthode du point d'entrée asynchrone sera traduite en appel
GetAwaitor().GetResult()
.Détails: https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main
ÉDITER:
Pour activer les fonctionnalités du langage C # 7.1, vous devez cliquer avec le bouton droit sur le projet et cliquer sur "Propriétés" puis aller dans l'onglet "Construire". Là, cliquez sur le bouton avancé en bas:
Dans le menu déroulant de la version linguistique, sélectionnez "7.1" (ou toute valeur supérieure):
La valeur par défaut est "la dernière version majeure" qui évaluerait (au moment d'écrire ces lignes) en C # 7.0, qui ne prend pas en charge async main dans les applications de console.
la source
J'ajouterai une caractéristique importante que toutes les autres réponses ont ignorée: l'annulation.
L'une des grandes choses dans TPL est la prise en charge de l'annulation, et les applications console ont une méthode d'annulation intégrée (CTRL + C). C'est très simple de les lier ensemble. Voici comment je structure toutes mes applications de console asynchrone:
la source
Wait()
?Wait()
, il n'attendra pas la fin du code asynchrone - il arrêtera d'attendre et mettra immédiatement fin au processus.Wait()
méthode reçoit le même jeton. Ce que j'essaie de dire, c'est que cela ne semble pas faire de différence.C # 7.1 (en utilisant vs 2017 update 3) présente async main
Tu peux écrire:
Pour plus de détails, série C # 7, partie 2: Async Main
Mise à jour:
Vous pouvez obtenir une erreur de compilation:
Cette erreur est due au fait que vs2017.3 est configuré par défaut en tant que c # 7.0 et non c # 7.1.
Vous devez modifier explicitement le paramètre de votre projet pour définir les fonctionnalités c # 7.1.
Vous pouvez définir c # 7.1 par deux méthodes:
Méthode 1: à l'aide de la fenêtre des paramètres du projet:
Méthode 2: modifier manuellement le PropertyGroup de .csproj
Ajouter cette propriété:
exemple:
la source
Si vous utilisez C # 7.1 ou version ultérieure, utilisez la réponse du nawfal et changez simplement le type de retour de votre méthode Main en
Task
ouTask<int>
. Si tu n'es pas:async Task MainAsync
comme Johan l'a dit ..GetAwaiter().GetResult()
pour attraper l'exception sous-jacente comme l'a dit do0g .CTRL+C
devrait mettre fin au processus immédiatement. (Merci binki !)OperationCancelledException
- retourne un code d'erreur approprié.Le code final ressemble à:
la source
e.Cancel = true
est inconditionnel.Je n'en ai pas encore eu besoin, mais quand j'ai utilisé une application console pour des tests rapides et asynchrone requis, je viens de le résoudre comme ceci:
la source
SynchronizationContext
associé au thread principal. Il neConfigureAwait(false)
bloquera donc pas, car même sans , toutes les continuations s'exécuteront sur le pool de threads.Pour appeler une tâche de manière asynchrone depuis Main, utilisez
Task.Run () pour .NET 4.5
Task.Factory.StartNew () pour .NET 4.0 (peut nécessiter la bibliothèque Microsoft.Bcl.Async pour async et attendre des mots clés)
Détails: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
la source
Dans Main, essayez de changer l'appel à GetList pour:
la source
Lorsque le C # 5 CTP a été introduit, vous avez certainement pu marquer principale avec
async
... même si ce n'était généralement pas une bonne idée de le faire. Je crois que cela a été changé par la sortie de VS 2013 pour devenir une erreur.Sauf si vous avez démarré d'autres threads de premier plan , votre programme se fermera à la
Main
fin, même s'il a commencé un travail en arrière-plan.Qu'essayez-vous vraiment de faire? Notez que votre
GetList()
méthode n'a vraiment pas besoin d'être asynchrone pour le moment - elle ajoute une couche supplémentaire sans raison réelle. C'est logiquement équivalent à (mais plus compliqué que):la source
DownloadTvChannels()
retour? Vraisemblablement, il renvoie unTask<List<TvChannel>>
n'est-ce pas? Sinon, il est peu probable que vous puissiez l'attendre. (Possible, étant donné le motif d'attente, mais peu probable.) Quant à laMain
méthode - elle doit toujours être statique ... avez-vous remplacé lestatic
modificateur par leasync
modificateur peut-être?public static async void Main() {}
? Mais siDownloadTvChannels()
renvoie déjà unTask<List<TvChannel>>
, il est probablement déjà asynchrone - vous n'avez donc pas besoin d'ajouter un autre calque. Cela vaut la peine de bien comprendre cela.La dernière version de C # - C # 7.1 permet de créer une application console asynchrone. Pour activer C # 7.1 dans le projet, vous devez mettre à niveau votre VS vers au moins 15.3 et changer la version C # en
C# 7.1
ouC# latest minor version
. Pour ce faire, accédez aux propriétés du projet -> Build -> Advanced -> Version linguistique.Après cela, le code suivant fonctionnera:
la source
Sur MSDN, la documentation de la méthode Task.Run (Action) fournit cet exemple qui montre comment exécuter une méthode de manière asynchrone à partir de
main
:Notez cette déclaration qui suit l'exemple:
Donc, si vous souhaitez que la tâche s'exécute sur le thread principal de l'application, consultez la réponse de @StephenCleary .
Et en ce qui concerne le fil sur lequel la tâche s'exécute, notez également le commentaire de Stephen sur sa réponse:
(Voir Gestion des exceptions (bibliothèque parallèle de tâches) pour savoir comment incorporer la gestion des exceptions pour gérer un problème
AggregateException
.)Enfin, sur MSDN à partir de la documentation de la méthode Task.Delay (TimeSpan) , cet exemple montre comment exécuter une tâche asynchrone qui renvoie une valeur:
Notez qu'au lieu de passer un
delegate
àTask.Run
, vous pouvez plutôt passer une fonction lambda comme ceci:la source
Pour éviter le gel lorsque vous appelez une fonction quelque part dans la pile des appels qui tente de rejoindre le thread actuel (qui est bloqué dans une attente), vous devez procéder comme suit:
(le casting n'est nécessaire que pour résoudre l'ambiguïté)
la source
Dans mon cas, j'avais une liste de tâches que je voulais exécuter en asynchrone à partir de ma méthode principale, j'utilisais cela en production depuis un certain temps et fonctionnait bien.
la source