Wie Sie festgestellt haben, lässt der Compiler in VS11 eine async Main
Methode nicht zu. Dies war in VS2010 mit dem Async CTP zulässig (aber nie empfohlen).
Ich habe kürzlich Blog-Beiträge über asynchrone / warten und insbesondere asynchrone Konsolenprogramme . Hier sind einige Hintergrundinformationen aus dem Intro-Beitrag:
Wenn "Warten" feststellt, dass das Warten nicht abgeschlossen ist, wird asynchron gehandelt. Es weist die Wartenden an, den Rest der Methode auszuführen, wenn sie abgeschlossen ist, und kehrt dann von der asynchronen Methode zurück. Await erfasst auch den aktuellen Kontext, wenn der Rest der Methode an das Wartende übergeben wird.
Später, wenn das Warten abgeschlossen ist, wird der Rest der asynchronen Methode (im erfassten Kontext) ausgeführt.
Hier ist der Grund, warum dies in Konsolenprogrammen ein Problem ist mit async Main
:
Denken Sie daran , von unserem Intro Post , dass ein asynchrones Verfahren wird zurückkehren , um seine Anrufer , bevor er abgeschlossen ist. Dies funktioniert perfekt in UI-Anwendungen (die Methode kehrt nur zur UI-Ereignisschleife zurück) und in ASP.NET-Anwendungen (die Methode kehrt vom Thread zurück, hält die Anforderung jedoch am Leben). Bei Konsolenprogrammen funktioniert es nicht so gut: Main kehrt zum Betriebssystem zurück - Ihr Programm wird also beendet.
Eine Lösung besteht darin, Ihren eigenen Kontext bereitzustellen - eine "Hauptschleife" für Ihr Konsolenprogramm, die asynchron kompatibel ist.
Wenn Sie eine Maschine mit dem Async CTP haben, können Sie GeneralThreadAffineContext
von My Documents \ Microsoft Visual Studio Async CTP \ Samples (C # Testing) Unit Testing \ AsyncTestUtilities . Alternativ können Sie AsyncContext
aus meinem Nito.AsyncEx NuGet-Paket verwenden .
Hier ist ein Beispiel mit AsyncContext
; GeneralThreadAffineContext
hat fast identische Verwendung:
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();
}
}
Alternativ können Sie den Hauptkonsolen-Thread einfach blockieren, bis Ihre asynchrone Arbeit abgeschlossen ist:
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();
}
}
Beachten Sie die Verwendung von GetAwaiter().GetResult()
; Dies vermeidet die AggregateException
Umhüllung, die auftritt, wenn Sie Wait()
oder verwenden Result
.
Update, 30.11.2017: Ab Visual Studio 2017 Update 3 (15.3) unterstützt die Sprache jetzt ein async Main
- solange es zurückgibt Task
oder Task<T>
. So können Sie jetzt Folgendes tun:
class Program
{
static async Task Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
Die Semantik scheint der GetAwaiter().GetResult()
Art des Blockierens des Hauptthreads zu entsprechen. Es gibt jedoch noch keine Sprachspezifikation für C # 7.1, daher ist dies nur eine Annahme.