Der Modifikator "async" kann in der Methode "Main" der Konsolenanwendung nicht angegeben werden

Ich bin neu in der asynchronen Programmierung mit dem async Modifizierer. Ich versuche herauszufinden, wie ich sicherstellen kann, dass meine Main Methode einer Konsolenanwendung asynchron läuft.

 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(); } } 

Ich weiß, dass dies nicht asynchron von "oben" durchgeführt wird. Da es nicht möglich ist, den async Modifizierer in der Main Methode anzugeben, wie kann ich den Code in Main asynchron ausführen?

326
09 февр. Set Danielovich 09 Feb 2012-02-09 13:13 '12 am 13:13 2012-02-09 13:13
@ 15 Antworten

Wie Sie herausgefunden haben, deaktiviert der Compiler in VS11 die async Main Hauptmethode. Dies war in VS2010 mit Async CTP zulässig (wurde jedoch nie empfohlen).

Ich habe kürzlich Blogeinträge über asynchrone / ausstehende und asynchrone Konsolenprogramme . Hier einige Hintergrundinformationen aus dem Einführungsartikel:

Wenn "wait" feststellt, dass das erwartete nicht abgeschlossen ist, wirkt es asynchron. Er berichtet, dass er den Rest der Methode nach dem Abschluss des Vorgangs fortsetzen wird und kehrt dann von der Async-Methode zurück. Das Warten wird auch den aktuellen Kontext korrigieren, wenn der Rest der Methode an die erwartete übergeben wird.

Später, wenn der erwartete Vorgang abgeschlossen ist, wird der Rest der asynchronen Methode (im erfassten Kontext) ausgeführt.

Aus diesem Grund ist dies ein Problem in Konsolenprogrammen mit async Main :

Beachten Sie in unserer einleitenden Nachricht, dass die async-Methode zu ihrem Aufrufer zurückkehrt, bevor sie abgeschlossen ist. Dies funktioniert problemlos in UI-Anwendungen (die Methode kehrt einfach zur UI-Ereignisschleife zurück) und ASP.NET-Anwendungen (die Methode gibt den Stream zurück, bleibt jedoch ausstehend). Dies funktioniert bei Konsolenprogrammen nicht so gut: Main kehrt zum Betriebssystem zurück - damit wird Ihr Programm beendet.

Eine Lösung besteht darin, Ihren eigenen Kontext bereitzustellen - die Hauptschleife für Ihr Konsolenprogramm, die mit asynchronen kompatibel ist.

Wenn Sie über einen Computer mit asynchronem CTP verfügen, können Sie den GeneralThreadAffineContext unter Eigene Dateien \ Microsoft Visual Studio Async-CTP \ Samples (C # -Test) Unit-Test \ AsyncTestUtilities verwenden. Außerdem können Sie den AsyncContext aus meinem Nito.AsyncEx-NuGet-Paket verwenden .

Hier ist ein Beispiel für die Verwendung von AsyncContext . GeneralThreadAffineContext fast dieselbe 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(); } } 

Außerdem können Sie den Hauptkonsolenstrom einfach blockieren, bis der asynchrone Vorgang 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() ; Das vermeidet das AggregateException Wrapping, das auftritt, wenn Sie Wait() oder Result .

Update, 2017-11-30: Ab Visual Studio 2017 Update 3 (15.3) unterstützt die Sprache jetzt async Main - wenn Task oder Task<T> . Jetzt können Sie das tun:

 class Program { static async Task Main(string[] args) { Bootstrapper bs = new Bootstrapper(); var list = await bs.GetList(); } } 

Die Semantik sieht genauso aus wie GetAwaiter().GetResult() für das Blockieren des Hauptthreads. Es gibt jedoch noch keine Sprachspezifikation für C # 7.1, daher ist dies nur eine Vermutung.

268
09 февр. Antwort erhalten an Stephen Cleary 09 Feb. 2012-02-09 17:09 '12 um 17:09 Uhr 2012-02-09 17:09

Sie können dies mit dieser einfachen Konstruktion lösen:

 class Program { static void Main(string[] args) { Task.Run(async () => { // Do any async anything you need here without worry }).GetAwaiter().GetResult(); } } 

Dadurch wird alles, was Sie tun, in einem ThreadPool gespeichert, wo Sie möchten (damit andere Tasks, die Sie starten / erwarten, nicht versuchen, erneut eine Verbindung zu einem Thread herzustellen, den sie nicht benötigen) und warten Sie, bis das Ende der Anwendung geschlossen ist. Keine speziellen Loops oder Außenlibs erforderlich.

Bearbeiten: Aktivieren Sie die Andrew-Lösung für nicht erfasste Ausnahmen.

308
07 июля '14 в 3:26 2014-07-07 03:26 Die Antwort wird von Chris Moschini am 7. Juli 14 am 3:26 2014-07-07 03:26 gegeben

Sie können dies auch ohne externe Bibliotheken tun, indem Sie folgende Schritte ausführen:

 class Program { static void Main(string[] args) { Bootstrapper bs = new Bootstrapper(); var getListTask = bs.GetList(); // returns the Task<List<TvChannel>> Task.WaitAll(getListTask); // block while the task completes var list = getListTask.Result; } } 
87
10 июля '13 в 22:34 2013-07-10 22:34 Die Antwort wird von Steven Evers am 10. Juli 13 um 22:34 Uhr 2013-07-10 22:34 gegeben

Ich füge eine wichtige Funktion hinzu, die alle anderen Antworten nicht berücksichtigt: Stornierung.

Eine der großartigsten Eigenschaften von TPL ist die Unterstützung für Abbrüche, während Konsolenanwendungen die Abbruchmethode (STRG + C) verwenden. Es ist sehr leicht, sie miteinander zu verbinden. So strukturiere ich alle meine asynchronen Konsolenanwendungen:

 static void Main(string[] args) { CancellationTokenSource cts = new CancellationTokenSource(); System.Console.CancelKeyPress += (s, e) => { e.Cancel = true; cts.Cancel(); }; MainAsync(args, cts.Token).Wait(); } static async Task MainAsync(string[] args, CancellationToken token) { ... } 
67
02 сент. Antwort von Cory Nelson 02 Sep 2014-09-02 23:54 14 am 23:54 2014-09-02 23:54

In C # 7.1 können Sie das korrekte asynchrone Main erstellen. Die entsprechenden Signaturen für die Main Methode wurden erweitert:

 public static Task Main(); public static Task<int> Main(); public static Task Main(string[] args); public static Task<int> Main(string[] args); 

Zum Beispiel können Sie Folgendes tun:

 static async Task Main(string[] args) { Bootstrapper bs = new Bootstrapper(); var list = await bs.GetList(); } 

Zur Kompilierzeit wird die asynchrone Eingabepunktmethode an den GetAwaitor().GetResult() .

Details: https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main

BEARBEITEN:

Um die Sprachfunktionen von C # 7.1 zu aktivieren, klicken Sie mit der rechten Maustaste auf das Projekt und klicken Sie auf "Eigenschaften". Wechseln Sie dann zur Registerkarte "Erstellen". Klicken Sie unten auf die Schaltfläche "Erweitert":

2019

30 мая '17 в 8:53 2017-05-30 08:53 Die Antwort wird von nawfal am 30. Mai 17 um 08:53 2017-05-30 08:53 gegeben

Es brauchte nicht viel mehr, aber als ich die Konsolenanwendung für schnelle Tests verwendete und async forderte, löste ich es einfach wie folgt:

 class Program { static void Main(string[] args) { MainAsync(args).Wait(); } static async Task MainAsync(string[] args) { // Code here } } 
18
02 сент. Antwort von Johan Falk 02 Sep. 2014-09-02 23:36 '14 um 23:36 2014-09-02 23:36

C # 7.1 (mit Update vs 2017 3) führt die asynchrone Hauptversion ein

Sie können schreiben:

  static async Task Main(string[] args) { await ... } 

Weitere Serie C # 7, Teil 2: Async Home

Update:

Möglicherweise wird ein Kompilierungsfehler angezeigt:

Das Programm enthält keine statische "Primary" -Methode, die für den Einstiegspunkt geeignet ist.

Dieser Fehler ist darauf zurückzuführen, dass vs2017.3 standardmäßig als C # 7.0 und nicht als C # 7.1 konfiguriert ist.

Sie müssen Ihr Projekt-Setup explizit ändern, um C # 7.1-Funktionen zu installieren.

Sie können C # 7.1 auf zwei Arten installieren:

Methode 1. Verwenden Sie das Fenster mit den Projekteinstellungen:

  • Öffnen Sie Ihre Projekteinstellungen
  • Wählen Sie die Registerkarte "Erstellen"
  • Klicken Sie auf "Erweitert".
  • Wählen Sie die gewünschte Version aus, wie in der folgenden Abbildung dargestellt:

2019

19 сент. Die Antwort wurde von Herrn Hassan am 19. September gegeben . 2017-09-19 05:02 '17 am 5:02 2017-09-19 05:02

Wenn Sie C # 7.1 oder höher verwenden , wechseln Sie zur Antwort von nawfal und ändern Sie einfach den Rückgabetyp Ihrer Main-Methode in Task oder Task<int> . Wenn Sie nicht

  • Haben Sie eine async Task MainAsync wie Johan sagte .
  • Rufen Sie es .GetAwaiter().GetResult() auf, um die Hauptausnahme abzufangen , beispielsweise do0g .
  • Support-Kündigung, wie Corey sagte .
  • Die zweite CTRL+C sollte den Vorgang sofort beenden. (Danke, binky !)
  • Handle OperationCancelledException - gibt den entsprechenden Fehlercode zurück.

Der endgültige Code sieht folgendermaßen aus:

 private static int Main(string[] args) { var cts = new CancellationTokenSource(); Console.CancelKeyPress += (s, e) => { e.Cancel = !cts.IsCancellationRequested; cts.Cancel(); }; try { return MainAsync(args, cts.Token).GetAwaiter().GetResult(); } catch (OperationCanceledException) { return 1223; // Cancelled. } } private static async Task<int> MainAsync(string[] args, CancellationToken cancellationToken) { // Your code... return await Task.FromResult(0); // Success. } 
15
06 сент. Antwort von Şafak Gür 06 Sep. 2016-09-06 16:22 '16 um 16:22 Uhr 2016-09-06 16:22

Verwenden Sie zum asynchronen Aufrufen einer Task aus Main

  • Task.Run () für .NET 4.5

  • Task.Factory.StartNew () für .NET 4.0 (Microsoft.Bcl.Async kann für asynchrone und ausstehende Schlüsselwörter erforderlich sein)

Lesen Sie mehr: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx

7
12 марта '14 в 0:14 2014-03-12 00:14 Die Antwort wird von user3408030 am 12. März '14 um 0:14 2014-03-12 00:14 gegeben

Ändern Sie grundsätzlich den Aufruf GetList in:

 Task.Run(() => bs.GetList()); 
4
07 февр. Die Antwort wird von mysticdotnet 07 Feb. gegeben. 2014-02-07 16:59 '14 um 16:59 2014-02-07 16:59

Wenn CTP C # 5 eingeführt wurde, können Sie Main sicherlich mit async kennzeichnen ... obwohl dies normalerweise keine gute Idee ist. Ich glaube, dass dies von VS 2013 geändert wurde, um ein Fehler zu werden.

Wenn Sie noch keine anderen Vordergrund-Threads gestartet haben, wird Ihr Programm nach Abschluss von Main beendet, auch wenn einige Hintergrundarbeiten gestartet wurden.

Was versuchst du wirklich zu tun? Beachten Sie, dass Ihre GetList() -Methode im Moment wirklich nicht asynchronisiert werden muss - eine zusätzliche Ebene wird aus irgendeinem Grund hinzugefügt. Es ist logisch gleichwertig (aber komplizierter):

 public Task<List<TvChannel>> GetList() { return new GetPrograms().DownloadTvChannels(); } 
4
09 февр. Die Antwort wird von Jon Skeet 09 Feb. gegeben. 2012-02-09 13:17 '12 am 13:17 2012-02-09 13:17

In MSDN enthält die Dokumentation für die Task.Run-Methode (Aktion) dieses Beispiel, das zeigt, wie eine asynchrone Methode von main :

@StephenCleary . 

Und im Hinblick auf den Thread, in dem die Aufgabe ausgeführt wird, beachten Sie auch Stephens Kommentar zu seiner Antwort:

Sie können einfaches Wait oder Result , und daran ist nichts falsch. Beachten Sie jedoch, dass es zwei wichtige Unterschiede gibt: 1) Alle async Fortsetzungen werden im Thread-Pool und nicht im Wesentlichen im Thread ausgeführt. 2) Alle Ausnahmen werden in AggregateException .

( Informationen zur Aktivierung der Ausnahmebehandlung für die Verarbeitung von AggregateException Sie unter Ausnahmebehandlung (Parallel Task Library) .)


In MSDN aus der Dokumentation für die Task.Delay-Methode (TimeSpan) schließlich zeigt dieses Beispiel, wie eine asynchrone Task gestartet wird, die einen Wert zurückgibt:

 var t = Task.Run(async () => { await Task.Delay(TimeSpan.FromSeconds(1.5)); return 42; }); 
3
16 мая '16 в 21:38 2016-05-16 21:38 Die Antwort wird von DavidRR am 16. Mai 16 um 21:38 2016-05-16 21:38 gegeben

Mit der neuen Version von C # - C # 7.1 können Sie eine Konsolenanwendung asynchron erstellen. Um C # 7.1 im Projekt zu aktivieren, müssen Sie VS auf mindestens 15.3 aktualisieren und die Version von C # auf C# 7.1 oder die C# latest minor version von C # ändern. Gehen Sie dazu zu Projekteigenschaften → Erstellen → Erweitert → Sprachversion.

Danach funktioniert der folgende Code:

 internal class Program { public static async Task Main(string[] args) { (...) } 
3
22 авг. Die Antwort ist Kedrzu 22 aug gegeben. 2017-08-22 15:01 '17 am 15:01 2017-08-22 15:01

Um das Auflegen zu verhindern, wenn Sie eine Funktion irgendwo nach unten aufrufen, den Aufrufstapel, der versucht, den aktuellen Thread erneut anzuhängen (der in Wait hängen bleibt), müssen Sie Folgendes tun:

 class Program { static void Main(string[] args) { Bootstrapper bs = new Bootstrapper(); List<TvChannel> list = Task.Run((Func<Task<List<TvChannel>>>)bs.GetList).Result; } } 

(Zwang nur zur Beseitigung der Mehrdeutigkeit erforderlich)

1
07 апр. Die Antwort wird von Nathan Phillips 07 Apr. gegeben. 2015-04-07 17:25 '15 um 17:25 Uhr 2015-04-07 17:25

In meinem Fall hatte ich eine Liste von Aufgaben, die ich asynchron von meiner Hauptmethode ausführen wollte. Ich habe sie >

 static void Main(string[] args) { Task.Run(async () => { await Task.WhenAll(jobslist.Select(nl => RunMulti(nl))); }).GetAwaiter().GetResult(); } private static async Task RunMulti(List<string> joblist) { await ... } 
1
26 июля '16 в 8:34 2016-07-26 08:34 Die Antwort wird von user_v am 26. Juli '16 um 8:34 2016-07-26 08:34 gegeben