Ich bin neu in der asynchronen Programmierung mit dem async
Modifikator. Ich versuche herauszufinden, wie ich sicherstellen kann, dass meine Main
Methode einer Konsolenanwendung tatsächlich asynchron ausgeführt wird.
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" läuft. Wie kann ich Code innerhalb von asynchron ausführen, da es nicht möglich ist, den async
Modifikator für die Main
Methode anzugeben main
?
c#
.net
asynchronous
console-application
Danielovich
quelle
quelle
Antworten:
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:
Hier ist der Grund, warum dies in Konsolenprogrammen ein Problem ist mit
async Main
: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 SieAsyncContext
aus meinem Nito.AsyncEx NuGet-Paket verwenden .Hier ist ein Beispiel mit
AsyncContext
;GeneralThreadAffineContext
hat fast identische Verwendung:Alternativ können Sie den Hauptkonsolen-Thread einfach blockieren, bis Ihre asynchrone Arbeit abgeschlossen ist:
Beachten Sie die Verwendung von
GetAwaiter().GetResult()
; Dies vermeidet dieAggregateException
Umhüllung, die auftritt, wenn SieWait()
oder verwendenResult
.Update, 30.11.2017: Ab Visual Studio 2017 Update 3 (15.3) unterstützt die Sprache jetzt ein
async Main
- solange es zurückgibtTask
oderTask<T>
. So können Sie jetzt Folgendes tun: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.quelle
Wait
oder verwendenResult
, und daran ist nichts auszusetzen. Beachten Sie jedoch, dass es zwei wichtige Unterschiede gibt: 1) Alleasync
Fortsetzungen werden im Thread-Pool und nicht im Haupt-Thread ausgeführt, und 2) Alle Ausnahmen werden in einen eingeschlossenAggregateException
.<LangVersion>latest</LangVersion>
ich sie wie hier gezeigt in die csproj-Datei einfügte .Sie können dies mit diesem einfachen Konstrukt lösen:
Dadurch wird alles, was Sie tun, auf dem ThreadPool dort abgelegt, wo Sie es möchten (andere Aufgaben, die Sie starten / abwarten, versuchen also nicht, einem Thread wieder beizutreten, sollten sie nicht), und warten, bis alles erledigt ist, bevor Sie die Konsolen-App schließen. Keine Notwendigkeit für spezielle Loops oder externe Bibliotheken.
Bearbeiten: Integrieren Sie Andrews Lösung für nicht erfasste Ausnahmen.
quelle
Wait()
mitGetAwaiter().GetResult()
Sie die vermeidenAggregateException
Wrapper , wenn die Dinge zu werfen.async main
Zeitpunkt in C # 7.1 eingeführt.Sie können dies auch tun, ohne externe Bibliotheken zu benötigen, indem Sie Folgendes tun:
quelle
getListTask.Result
auch ein blockierender Aufruf ist und der obige Code daher ohne geschrieben werden kannTask.WaitAll(getListTask)
.GetList
Sie Würfe auslösen, müssen SieAggregateException
eine Ausnahme abfangen und abfragen, um die tatsächlich ausgelöste Ausnahme zu ermitteln. Sie können jedoch anrufenGetAwaiter()
, um dasTaskAwaiter
für das zu erhaltenTask
, und das anrufenGetResult()
, dhvar list = getListTask.GetAwaiter().GetResult();
. Wenn Sie das Ergebnis desTaskAwaiter
(auch blockierenden) Aufrufs erhalten, werden alle ausgelösten Ausnahmen nicht in eine eingeschlossenAggregateException
.In C # 7.1 können Sie einen ordnungsgemäßen asynchronen Main ausführen . Die entsprechenden Signaturen für die
Main
Methode wurden erweitert auf:Zum Beispiel könnten Sie tun:
Zur Kompilierungszeit wird die asynchrone Einstiegspunktmethode in call übersetzt
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, müssen Sie mit der rechten Maustaste auf das Projekt klicken und auf "Eigenschaften" klicken. Wechseln Sie dann zur Registerkarte "Erstellen". Klicken Sie dort unten auf die Schaltfläche Erweitert:
Wählen Sie im Dropdown-Menü Sprachversion "7.1" (oder einen höheren Wert) aus:
Die Standardeinstellung ist "neueste Hauptversion", die (zum Zeitpunkt dieses Schreibens) C # 7.0 ergibt, das in Konsolen-Apps keine asynchrone Hauptversion unterstützt.
quelle
Ich werde eine wichtige Funktion hinzufügen, die alle anderen Antworten übersehen haben: Stornierung.
Eines der großen Dinge in TPL ist die Stornierungsunterstützung, und in Konsolen-Apps ist eine Stornierungsmethode integriert (STRG + C). Es ist sehr einfach, sie zusammenzubinden. So strukturiere ich alle meine asynchronen Konsolen-Apps:
quelle
Wait()
werden?Wait()
, wird es nicht warten, bis der asynchrone Code beendet ist - es wird aufhören zu warten und den Vorgang sofort beenden.Wait()
Methode das gleiche Token übergeben wird. Ich versuche zu sagen, dass es keinen Unterschied zu machen scheint.C # 7.1 (mit vs 2017 Update 3) führt async main ein
Du kannst schreiben:
Weitere Informationen zur C # 7-Serie, Teil 2: Async Main
Aktualisieren:
Möglicherweise wird ein Kompilierungsfehler angezeigt:
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 sollten die Einstellung Ihres Projekts explizit ändern, um c # 7.1-Funktionen festzulegen.
Sie können c # 7.1 auf zwei Arten einstellen:
Methode 1: Verwenden des Projekteinstellungsfensters:
Methode 2: Ändern Sie die PropertyGroup von .csproj manuell
Fügen Sie diese Eigenschaft hinzu:
Beispiel:
quelle
Wenn Sie C # 7.1 oder höher verwenden, gehen Sie zur Antwort des Nawfals und ändern Sie einfach den Rückgabetyp Ihrer Main-Methode in
Task
oderTask<int>
. Wenn Sie nicht sind:async Task MainAsync
wie Johan gesagt ..GetAwaiter().GetResult()
auf, um die zugrunde liegende Ausnahme abzufangen, wie do0g sagte .CTRL+C
sollte den Prozess sofort beenden. (Danke Binki !)OperationCancelledException
- Gibt einen entsprechenden Fehlercode zurück.Der endgültige Code sieht folgendermaßen aus:
quelle
e.Cancel = true
unbedingt erforderlich ist.Ich habe noch nicht so viel gebraucht, aber als ich die Konsolenanwendung für Schnelltests verwendet und Async benötigt habe, habe ich es einfach so gelöst:
quelle
SynchronizationContext
dem Hauptthread nichts zugeordnet. Es wird also kein Deadlock auftreten, da auch ohneConfigureAwait(false)
alle Fortsetzungen im Threadpool ausgeführt werden.Verwenden Sie zum asynchronen Aufrufen einer Aufgabe von Main aus
Task.Run () für .NET 4.5
Task.Factory.StartNew () für .NET 4.0 (erfordert möglicherweise die Microsoft.Bcl.Async-Bibliothek für asynchrone und warten auf Schlüsselwörter)
Details: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
quelle
Versuchen Sie in Main, den Aufruf von GetList in Folgendes zu ändern:
quelle
Wenn die C # 5 CTP eingeführt wurde, werden Sie sicherlich könnten markieren Haupt mit
async
... obwohl es in der Regel keine gute Idee war , dies zu tun. Ich glaube, dies wurde durch die Veröffentlichung von VS 2013 geändert, um ein Fehler zu werden.Sofern Sie keine anderen Vordergrund- Threads gestartet haben , wird Ihr Programm nach Abschluss
Main
beendet, selbst wenn einige Hintergrundarbeiten gestartet wurden.Was versuchst du wirklich zu tun? Beachten Sie, dass Ihre
GetList()
Methode im Moment wirklich nicht asynchron sein muss - sie fügt ohne wirklichen Grund eine zusätzliche Ebene hinzu. Es ist logisch äquivalent zu (aber komplizierter als):quelle
DownloadTvChannels()
zurück? Vermutlich gibt es einTask<List<TvChannel>>
nicht wahr? Wenn nicht, ist es unwahrscheinlich, dass Sie darauf warten können. (Möglich, das Erwartenden Muster gegeben, aber unwahrscheinlich.) Was dasMain
Verfahren - es muss noch statisch sein ... haben Sie ersetzen denstatic
Modifikator mit demasync
Modifikator vielleicht?public static async void Main() {}
? Wenn jedochDownloadTvChannels()
bereits a zurückgegeben wirdTask<List<TvChannel>>
, ist es vermutlich bereits asynchron - Sie müssen also keine weitere Ebene hinzufügen. Es lohnt sich, dies sorgfältig zu verstehen.Die neueste Version von C # - C # 7.1 ermöglicht das Erstellen einer asynchronen Konsolen-App. Um C # 7.1 im Projekt zu aktivieren, müssen Sie Ihren VS auf mindestens 15.3 aktualisieren und die C # -Version auf
C# 7.1
oder ändernC# latest minor version
. Gehen Sie dazu zu Projekteigenschaften -> Erstellen -> Erweitert -> Sprachversion.Danach funktioniert folgender Code:
quelle
Unter MSDN enthält die Dokumentation zur Task.Run-Methode (Aktion) dieses Beispiel, das zeigt, wie eine Methode asynchron ausgeführt wird von
main
:Beachten Sie diese Aussage, die dem Beispiel folgt:
Wenn Sie stattdessen möchten, dass die Aufgabe im Hauptanwendungsthread ausgeführt wird, lesen Sie die Antwort von @StephenCleary .
Beachten Sie in Bezug auf den Thread, in dem die Aufgabe ausgeführt wird, auch Stephens Kommentar zu seiner Antwort:
( Informationen zur Integration der Ausnahmebehandlung für den Umgang mit einer Ausnahme finden Sie unter Ausnahmebehandlung (Task Parallel Library)
AggregateException
.)In MSDN aus der Dokumentation zur Task.Delay-Methode (TimeSpan) wird in diesem Beispiel gezeigt, wie eine asynchrone Task ausgeführt wird, die einen Wert zurückgibt:
Beachten Sie, dass stattdessen eine die Weitergabe
delegate
anTask.Run
, Sie stattdessen eine Lambda - Funktion wie diese passieren kann:quelle
Um ein Einfrieren zu vermeiden, wenn Sie eine Funktion irgendwo im Aufrufstapel aufrufen, die versucht, den aktuellen Thread erneut zu verbinden (der in einer Wartezeit steckt), müssen Sie Folgendes tun:
(Die Besetzung ist nur erforderlich, um Mehrdeutigkeiten aufzulösen.)
quelle
In meinem Fall hatte ich eine Liste von Jobs, die ich von meiner Hauptmethode aus asynchron ausführen wollte, die ich seit einiger Zeit in der Produktion verwende und die einwandfrei funktioniert.
quelle