Der Modifikator 'async' kann in der 'Main'-Methode einer Konsolen-App nicht angegeben werden

444

Ich bin neu in der asynchronen Programmierung mit dem asyncModifikator. Ich versuche herauszufinden, wie ich sicherstellen kann, dass meine MainMethode 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 asyncModifikator für die MainMethode anzugeben main?

Danielovich
quelle
22
Dies ist in C # 7.1 nicht mehr der Fall. Hauptmethoden können
asynchron
2
Hier ist die Ankündigung des C # 7.1-Blogposts . Siehe den Abschnitt mit dem Titel Async Main .
Styfle

Antworten:

381

Wie Sie festgestellt haben, lässt der Compiler in VS11 eine async MainMethode 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 GeneralThreadAffineContextvon My Documents \ Microsoft Visual Studio Async CTP \ Samples (C # Testing) Unit Testing \ AsyncTestUtilities . Alternativ können Sie AsyncContextaus meinem Nito.AsyncEx NuGet-Paket verwenden .

Hier ist ein Beispiel mit AsyncContext; GeneralThreadAffineContexthat 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 AggregateExceptionUmhü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 Taskoder 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.

Stephen Cleary
quelle
30
Sie können ein einfaches Waitoder verwenden Result, und daran ist nichts auszusetzen. Beachten Sie jedoch, dass es zwei wichtige Unterschiede gibt: 1) Alle asyncFortsetzungen werden im Thread-Pool und nicht im Haupt-Thread ausgeführt, und 2) Alle Ausnahmen werden in einen eingeschlossen AggregateException.
Stephen Cleary
2
Hatte bis dahin ein echtes Problem damit, dies herauszufinden (und Ihren Blog-Beitrag). Dies ist bei weitem die einfachste Methode, um dieses Problem zu lösen, und Sie können das Paket in der Nuget-Konsole mit nur einem "Installationspaket Nito.Asyncex" installieren, und Sie sind fertig.
ConstantineK
1
@ StephenCleary: Danke für die schnelle Antwort Stephen. Ich verstehe nicht, warum niemand möchte, dass der Debugger kaputt geht, wenn eine Ausnahme ausgelöst wird. Wenn ich debugge und auf eine Nullreferenzausnahme stoße, wird es bevorzugt, direkt zur fehlerhaften Codezeile zu wechseln. VS funktioniert so "out of the box" für synchronen Code, aber nicht für async / await.
Greg
6
C # 7.1 hat jetzt eine asynchrone Hauptleitung, die es möglicherweise wert ist, zu Ihrer großartigen Antwort hinzugefügt zu werden: @StephenCleary github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/…
Mafii
3
Wenn Sie die C # 7.1-Version in VS 2017 verwenden, musste ich sicherstellen, dass das Projekt für die Verwendung der neuesten Version der Sprache konfiguriert wurde, indem <LangVersion>latest</LangVersion>ich sie wie hier gezeigt in die csproj-Datei einfügte .
Liam
359

Sie können dies mit diesem einfachen Konstrukt 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, 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.

Chris Moschini
quelle
3
Dieser Ansatz ist sehr offensichtlich, neigt jedoch dazu, Ausnahmen zu verpacken, sodass ich jetzt nach einem besseren Weg suche.
Abatishchev
2
@abatishchev Sie sollten try / catch in Ihrem Code verwenden, zumindest innerhalb der Task. Führen Sie diese aus, wenn nicht genauer, und lassen Sie keine Ausnahmen zur Task schweben. Sie vermeiden das Wrap-Up-Problem, indem Sie try / catch um Dinge legen, die fehlschlagen können.
Chris Moschini
54
Wenn Sie ersetzen Wait()mit GetAwaiter().GetResult()Sie die vermeiden AggregateExceptionWrapper , wenn die Dinge zu werfen.
Andrew Arnott
7
So wird zum jetzigen async mainZeitpunkt in C # 7.1 eingeführt.
user9993
@ user9993 Nach diesem Vorschlag ist das nicht genau richtig.
Sinjai
90

Sie können dies auch tun, ohne externe Bibliotheken zu benötigen, indem Sie Folgendes tun:

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;
    }
}
Steven Evers
quelle
7
Denken Sie daran, dass dies getListTask.Resultauch ein blockierender Aufruf ist und der obige Code daher ohne geschrieben werden kann Task.WaitAll(getListTask).
do0g
27
Wenn GetListSie Würfe auslösen, müssen Sie AggregateExceptioneine Ausnahme abfangen und abfragen, um die tatsächlich ausgelöste Ausnahme zu ermitteln. Sie können jedoch anrufen GetAwaiter(), um das TaskAwaiterfür das zu erhalten Task, und das anrufen GetResult(), dh var list = getListTask.GetAwaiter().GetResult();. Wenn Sie das Ergebnis des TaskAwaiter(auch blockierenden) Aufrufs erhalten, werden alle ausgelösten Ausnahmen nicht in eine eingeschlossen AggregateException.
do0g
1
GetAwaiter () GetResult war die Antwort, die ich brauchte. Das funktioniert perfekt für das, was ich versucht habe. Ich werde dies wahrscheinlich auch an anderen Orten verwenden.
Todespirscher
78

In C # 7.1 können Sie einen ordnungsgemäßen asynchronen Main ausführen . Die entsprechenden Signaturen für die MainMethode wurden erweitert auf:

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önnten Sie tun:

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

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:

Geben Sie hier die Bildbeschreibung ein

Wählen Sie im Dropdown-Menü Sprachversion "7.1" (oder einen höheren Wert) aus:

Geben Sie hier die Bildbeschreibung ein

Die Standardeinstellung ist "neueste Hauptversion", die (zum Zeitpunkt dieses Schreibens) C # 7.0 ergibt, das in Konsolen-Apps keine asynchrone Hauptversion unterstützt.

nawfal
quelle
2
FWIW dies ist in Visual Studio 15.3 und höher verfügbar, das derzeit als Beta / Vorschau-Version von hier verfügbar ist: visualstudio.com/vs/preview
Mahmoud Al-Qudsi
Moment mal ... Ich führe eine vollständig aktualisierte Installation aus und meine neueste Option ist 7.1 ... wie haben Sie 7.2 bereits im Mai erhalten?
Mai Antwort war meine. Die Oktober-Bearbeitung wurde von jemand anderem durchgeführt, als ich glaube, dass 7.2 (Vorschau?) Veröffentlicht wurde.
Nawfal
1
Heads up - überprüfen Sie, ob es auf allen Konfigurationen vorhanden ist, und debuggen Sie nicht nur, wenn Sie dies tun!
user230910
1
@ user230910 danke. Eine der seltsamsten Entscheidungen des c # -Teams.
Nawfal
74

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:

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)
{
    ...
}
Cory Nelson
quelle
Sollte der Stornierungsschein auch an den übergeben Wait()werden?
Siewers
5
Nein, da Sie möchten, dass der asynchrone Code die Stornierung ordnungsgemäß handhaben kann. Wenn Sie es an übergeben Wait(), wird es nicht warten, bis der asynchrone Code beendet ist - es wird aufhören zu warten und den Vorgang sofort beenden.
Cory Nelson
Bist du dir da sicher? Ich habe es gerade ausprobiert und es scheint, dass die Stornierungsanforderung auf der tiefsten Ebene verarbeitet wird, selbst wenn die Wait()Methode das gleiche Token übergeben wird. Ich versuche zu sagen, dass es keinen Unterschied zu machen scheint.
Siewers
4
Ich bin sicher. Sie möchten die Operation selbst abbrechen und nicht warten, bis die Operation beendet ist. Es sei denn, Sie kümmern sich nicht um die Fertigstellung des Bereinigungscodes oder das Ergebnis davon.
Cory Nelson
1
Ja, ich glaube ich verstehe, es schien einfach keinen Unterschied in meinem Code zu machen. Eine andere Sache, die mich vom Kurs abwarf, war ein höflicher ReSharper-Hinweis auf die Wartemethode, die das Abbrechen unterstützt;) Vielleicht möchten Sie einen Try-Catch in das Beispiel aufnehmen, da dadurch eine OperationCancelledException
ausgelöst
22

C # 7.1 (mit vs 2017 Update 3) führt async main ein

Du kannst schreiben:

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

Weitere Informationen zur C # 7-Serie, Teil 2: Async Main

Aktualisieren:

Möglicherweise wird ein Kompilierungsfehler angezeigt:

Das Programm enthält keine statische 'Main'-Methode, die für einen 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 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:

  • Öffnen Sie die Einstellungen Ihres Projekts
  • Wählen Sie die Registerkarte Erstellen
  • Klicken Sie auf die Schaltfläche Erweitert
  • Wählen Sie die gewünschte Version aus. Wie in der folgenden Abbildung dargestellt:

Geben Sie hier die Bildbeschreibung ein

Methode 2: Ändern Sie die PropertyGroup von .csproj manuell

Fügen Sie diese Eigenschaft hinzu:

    <LangVersion>7.1</LangVersion>

Beispiel:

    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <PlatformTarget>AnyCPU</PlatformTarget>
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <Optimize>false</Optimize>
        <OutputPath>bin\Debug\</OutputPath>
        <DefineConstants>DEBUG;TRACE</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <Prefer32Bit>false</Prefer32Bit>
        <LangVersion>7.1</LangVersion>
    </PropertyGroup>    
M. Hassan
quelle
20

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 Taskoder Task<int>. Wenn Sie nicht sind:

  • Habe ein async Task MainAsync wie Johan gesagt .
  • Rufen Sie its .GetAwaiter().GetResult()auf, um die zugrunde liegende Ausnahme abzufangen, wie do0g sagte .
  • Support-Stornierung wie Cory sagte .
  • Eine Sekunde CTRL+Csollte den Prozess sofort beenden. (Danke Binki !)
  • Handle OperationCancelledException- Gibt einen 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.
}
Şafak Gür
quelle
1
Viele nette Programme brechen CancelKeyPress nur beim ersten Mal ab, so dass, wenn Sie ^ C einmal drücken, Sie ein ordnungsgemäßes Herunterfahren erhalten, aber wenn Sie ungeduldig sind, ein zweites ^ C unansehnlich beendet wird. Bei dieser Lösung müssen Sie das Programm manuell beenden, wenn das CancellationToken nicht berücksichtigt wird, da dies e.Cancel = trueunbedingt erforderlich ist.
Binki
19

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:

class Program
{
    static void Main(string[] args)
    {
        MainAsync(args).Wait();
    }

    static async Task MainAsync(string[] args)
    {
        // Code here
    }
}
Johan Falk
quelle
Dieses Beispiel funktioniert falsch, wenn Sie die Aufgabe im aktuellen Kontext planen und dann warten müssen (Sie können beispielsweise vergessen, ConfigureAwait (false) hinzuzufügen, sodass die Rückgabemethode im Hauptthread geplant wird, der sich in der Funktion "Warten" befindet ). Da sich der aktuelle Thread im Wartezustand befindet, erhalten Sie einen Deadlock.
Manushin Igor
6
Nicht wahr, @ManushinIgor. Zumindest in diesem trivialen Beispiel ist SynchronizationContextdem Hauptthread nichts zugeordnet. Es wird also kein Deadlock auftreten, da auch ohne ConfigureAwait(false)alle Fortsetzungen im Threadpool ausgeführt werden.
Andrew Arnott
4

Versuchen Sie in Main, den Aufruf von GetList in Folgendes zu ändern:

Task.Run(() => bs.GetList());
mysticdotnet
quelle
4

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 Mainbeendet, 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):

public Task<List<TvChannel>> GetList()
{
    return new GetPrograms().DownloadTvChannels();
}
Jon Skeet
quelle
2
Jon, ich möchte die Elemente in der Liste asynchron abrufen. Warum ist die Asynchronisierung für diese GetList-Methode nicht geeignet? Liegt es daran, dass ich die Elemente in der Liste asynchron sammeln muss und nicht die Liste selbst? Wenn ich versuche, die Hauptmethode mit asynchron zu markieren, erhalte ich eine "enthält keine statische Hauptmethode ..."
Danielovich
@danielovich: Was kommt DownloadTvChannels()zurück? Vermutlich gibt es ein Task<List<TvChannel>>nicht wahr? Wenn nicht, ist es unwahrscheinlich, dass Sie darauf warten können. (Möglich, das Erwartenden Muster gegeben, aber unwahrscheinlich.) Was das MainVerfahren - es muss noch statisch sein ... haben Sie ersetzen den staticModifikator mit dem asyncModifikator vielleicht?
Jon Skeet
Ja, es gibt eine Aufgabe <..> zurück, wie Sie gesagt haben. Egal wie ich versuche, Async in die Main-Methodensignatur einzufügen, es wird ein Fehler ausgegeben. Ich sitze auf VS11 Preview Bits!
Danielovich
@danielovich: Auch mit einem ungültigen Rückgabetyp? Nur public static async void Main() {}? Wenn jedoch DownloadTvChannels()bereits a zurückgegeben wird Task<List<TvChannel>>, ist es vermutlich bereits asynchron - Sie müssen also keine weitere Ebene hinzufügen. Es lohnt sich, dies sorgfältig zu verstehen.
Jon Skeet
1
@nawfal: Rückblickend denke ich, dass es sich geändert hat, bevor VS2013 veröffentlicht wurde. Ich bin mir nicht sicher, ob C # 7 das ändern wird ...
Jon Skeet
4

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.1oder ändern C# latest minor version. Gehen Sie dazu zu Projekteigenschaften -> Erstellen -> Erweitert -> Sprachversion.

Danach funktioniert folgender Code:

internal class Program
{
    public static async Task Main(string[] args)
    {
         (...)
    }
Kedrzu
quelle
3

Unter MSDN enthält die Dokumentation zur Task.Run-Methode (Aktion) dieses Beispiel, das zeigt, wie eine Methode asynchron ausgeführt wird von main:

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
    public static void Main()
    {
        ShowThreadInfo("Application");

        var t = Task.Run(() => ShowThreadInfo("Task") );
        t.Wait();
    }

    static void ShowThreadInfo(String s)
    {
        Console.WriteLine("{0} Thread ID: {1}",
                          s, Thread.CurrentThread.ManagedThreadId);
    }
}
// The example displays the following output:
//       Application thread ID: 1
//       Task thread ID: 3

Beachten Sie diese Aussage, die dem Beispiel folgt:

Die Beispiele zeigen, dass die asynchrone Task auf einem anderen Thread als dem Hauptanwendungsthread ausgeführt wird.

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:

Sie können ein einfaches Waitoder verwenden Result, und daran ist nichts auszusetzen. Beachten Sie jedoch, dass es zwei wichtige Unterschiede gibt: 1) Alle asyncFortsetzungen werden im Thread-Pool und nicht im Haupt-Thread ausgeführt, und 2) Alle Ausnahmen werden in einen eingeschlossen AggregateException.

( 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:

using System;
using System.Threading.Tasks;

public class Example
{
    public static void Main()
    {
        var t = Task.Run(async delegate
                {
                    await Task.Delay(TimeSpan.FromSeconds(1.5));
                    return 42;
                });
        t.Wait();
        Console.WriteLine("Task t Status: {0}, Result: {1}",
                          t.Status, t.Result);
    }
}
// The example displays the following output:
//        Task t Status: RanToCompletion, Result: 42

Beachten Sie, dass stattdessen eine die Weitergabe delegatean Task.Run, Sie stattdessen eine Lambda - Funktion wie diese passieren kann:

var t = Task.Run(async () =>
        {
            await Task.Delay(TimeSpan.FromSeconds(1.5));
            return 42;
        });
DavidRR
quelle
1

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:

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

(Die Besetzung ist nur erforderlich, um Mehrdeutigkeiten aufzulösen.)

Nathan Phillips
quelle
Vielen Dank; Task.Run verursacht nicht den Deadlock von GetList (). Warten Sie, diese Antwort sollte mehr positive Stimmen haben ...
Stefano d'Antonio
1

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.

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 ...
}
user_v
quelle