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:
Si "attendre" voit que l'attente n'est pas terminée, alors il agit de manière asynchrone. Il indique à l'attente d'exécuter le reste de la méthode lorsqu'elle se termine, puis revient de la méthode async. Await capturera également le contexte actuel lorsqu'il passera le reste de la méthode à l'attendu.
Plus tard, à la fin de l'attente, il exécutera le reste de la méthode async (dans le contexte capturé).
Voici pourquoi il s'agit d'un problème dans les programmes de console avec un async Main
:
Rappelez-vous de notre post d'introduction qu'une méthode async retournera à son appelant avant d'être terminée. Cela fonctionne parfaitement dans les applications d'interface utilisateur (la méthode revient simplement à la boucle d'événement d'interface utilisateur) et les applications ASP.NET (la méthode renvoie le thread mais maintient la demande en vie). Cela ne fonctionne pas si bien pour les programmes de console: Main revient sur le système d'exploitation - donc votre programme se ferme.
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:
using Nito.AsyncEx;
class Program
{
static void Main(string[] args)
{
AsyncContext.Run(() => MainAsync(args));
}
static async void MainAsync(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
Alternativement, vous pouvez simplement bloquer le thread principal de la console jusqu'à ce que votre travail asynchrone soit terminé:
class Program
{
static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult();
}
static async Task MainAsync(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
Notez l'utilisation de GetAwaiter().GetResult()
; cela évite le AggregateException
wrapping qui se produit si vous utilisez Wait()
ou Result
.
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 renvoie Task
ou Task<T>
. Vous pouvez donc maintenant faire ceci:
class Program
{
static async Task Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
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.