Wie kann ich eine asynchrone Methode in Main aufrufen?

77
public class test
{
    public async Task Go()
    {
        await PrintAnswerToLife();
        Console.WriteLine("done");
    }

    public async Task PrintAnswerToLife()
    {
        int answer = await GetAnswerToLife();
        Console.WriteLine(answer);
    }

    public async Task<int> GetAnswerToLife()
    {
        await Task.Delay(5000);
        int answer = 21 * 2;
        return answer;
    }
}

Wie kann ich das tun, wenn ich Go in der main () -Methode aufrufen möchte? Ich probiere neue Funktionen aus. Ich weiß, dass ich die asynchrone Methode mit einem Ereignis verknüpfen kann. Durch Auslösen dieses Ereignisses kann die asynchrone Methode aufgerufen werden.

Aber was ist, wenn ich es direkt in der Hauptmethode aufrufen möchte? Wie kann ich das machen?

Ich habe so etwas gemacht

class Program
{
    static void Main(string[] args)
    {
        test t = new test();
        t.Go().GetAwaiter().OnCompleted(() =>
        {
            Console.WriteLine("finished");
        });
        Console.ReadKey();
    }


}

Aber es scheint ein totes Schloss zu sein und nichts wird auf dem Bildschirm gedruckt.

Larry
quelle
Anscheinend habe ich das Problem gefunden, denn GetAwaiter (). OnCompleted () kehrt sofort zur Hauptfunktion zurück. Wenn also Console.Readkey () aufgerufen wird, blockiert der Hauptthread, sodass die von der Task zurückgegebene Ausgabemeldung nicht auf dem Bildschirm gedruckt werden kann Weil es darauf wartet, dass der Haupt-Thread entsperrt wird. Wenn ich Console.readkey () durch while (true) ersetze {Thread.Sleep (1000); } es funktioniert gut.
Larry
Ich kann Ihr Deadlock-Problem nicht reproduzieren, aber wenn es jemanden interessiert, versuchen Sie Go().ConfigureAwait(false).GetAwaiter(), einen neuen Kontext innerhalb der asynchronen Methode zu verwenden.
Arviman

Antworten:

100

Ihre MainMethode kann vereinfacht werden. Für C # 7.1 und neuer:

static async Task Main(string[] args)
{
    test t = new test();
    await t.Go();
    Console.WriteLine("finished");
    Console.ReadKey();
}

Für frühere Versionen von C #:

static void Main(string[] args)
{
    test t = new test();
    t.Go().Wait();
    Console.WriteLine("finished");
    Console.ReadKey();
}

Dies ist Teil der Schönheit des asyncSchlüsselworts (und der zugehörigen Funktionalität): Die Verwendung und Verwirrung von Rückrufen wird stark reduziert oder beseitigt.

Tim S.
quelle
@MarcGravell Ich habe den neuen Ansatz in meine Antwort aufgenommen. Die Antwort von DavidG enthält etwas mehr Details, aber ich bin nicht mehr veraltet. 👍
Tim S.
Möglicherweise müssen Sie Ihre Projektdatei ändern, um die Verwendung von c # 7.0 und höher zu ermöglichen. Antwort hier
Luke
26

Anstelle von Warten sollten Sie besser verwenden, new test().Go().GetAwaiter().GetResult() da dadurch vermieden wird, dass Ausnahmen in AggregateExceptions eingeschlossen werden. Sie können Ihre Go () -Methode also wie gewohnt mit einem try catch-Block (Exception ex) umgeben.

Arviman
quelle
@ MarkAlicz Es
DavidG
Rufen Sie GetResult () nicht für eine asynchrone Methode auf. Dadurch wird der Haupt-Thread blockiert. Dies macht nicht nur den Zweck asynchroner Methoden zunichte, sondern lässt auch Raum für Deadlocks.
Ruchira
21

Seit der Veröffentlichung von C # v7.1 stehen async mainMethoden zur Verfügung, die die Problemumgehungen in den bereits veröffentlichten Antworten überflüssig machen. Die folgenden Signaturen wurden hinzugefügt:

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

Auf diese Weise können Sie Ihren Code folgendermaßen schreiben:

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

static async Task DoSomethingAsync()
{
    //...
}
DavidG
quelle
2
Ich versuche es mit static async Task Main(string[] args), aber ich bekomme den Fehler CS5001 Program does not contain a static 'Main' method suitable for an entry point. Ich habe die Projekteigenschaften überprüft und es ist kein Startobjekt in der Dropdown-Liste verfügbar. Verwenden des neuesten Updates von VS2017 + .NET Core 2.0. Wie überwinde ich das?
NightOwl888
@ NightOwl888 Können Sie C # 7.1 in den Projekteigenschaften sehen?
DavidG
4
Ja, ich kann, danke. Die Standardeinstellung ist "neueste Hauptversion", daher war die Standardeinstellung 7.0. Ich habe auf 7.1 gewechselt und es wird jetzt kompiliert.
NightOwl888
12
class Program
{
    static void Main(string[] args)
    {
       test t = new test();
       Task.Run(async () => await t.Go());
    }
}
maxspan
quelle
1
Diese Antwort erstellt einen Hintergrund-Thread, der Probleme verursachen kann, wenn der Vordergrund-Thread zuerst abgeschlossen wird
Luke
11

Solange Sie über die zurückgegebene Aufgabe auf das Ergebnisobjekt zugreifen, müssen Sie GetAwaiter überhaupt nicht verwenden (nur für den Fall, dass Sie auf das Ergebnis zugreifen).

static async Task<String> sayHelloAsync(){

       await Task.Delay(1000);
       return "hello world";

}

static void main(string[] args){

      var data = sayHelloAsync();
      //implicitly waits for the result and makes synchronous call. 
      //no need for Console.ReadKey()
      Console.Write(data.Result);
      //synchronous call .. same as previous one
      Console.Write(sayHelloAsync().GetAwaiter().GetResult());

}

Wenn Sie warten möchten, bis eine Aufgabe erledigt ist, und eine weitere Verarbeitung durchführen möchten:

sayHelloAsyn().GetAwaiter().OnCompleted(() => {
   Console.Write("done" );
});
Console.ReadLine();

Wenn Sie daran interessiert sind, die Ergebnisse von sayHelloAsync zu erhalten und weiter zu verarbeiten:

sayHelloAsync().ContinueWith(prev => {
   //prev.Result should have "hello world"
   Console.Write("done do further processing here .. here is the result from sayHelloAsync" + prev.Result);
});
Console.ReadLine();

Eine letzte einfache Möglichkeit, auf die Funktion zu warten:

static void main(string[] args){
  sayHelloAsync().Wait();
  Console.Read();
}

static async Task sayHelloAsync(){          
  await Task.Delay(1000);
  Console.Write( "hello world");

}
Muhammad Soliman
quelle
Dies erfordert weitere Informationen darüber, welche Methode zu wählen ist und warum
Joe Phillips
4
public static void Main(string[] args)
{
    var t = new test();
    Task.Run(async () => { await t.Go();}).Wait();
}
Sami Hussain
quelle
4
Geben Sie bei der Beantwortung immer an, warum die gestellte Frage "beantwortet" wird.
Prasoon Karunan V
1

Verwenden Sie .Wait ()

static void Main(string[] args){
   SomeTaskManager someTaskManager  = new SomeTaskManager();
   Task<List<String>> task = Task.Run(() => marginaleNotesGenerationTask.Execute());
   task.Wait();
   List<String> r = task.Result;
} 

public class SomeTaskManager
{
    public async Task<List<String>> Execute() {
        HttpClient client = new HttpClient();
        client.BaseAddress = new Uri("http://localhost:4000/");     
        client.DefaultRequestHeaders.Accept.Clear();           
        HttpContent httpContent = new StringContent(jsonEnvellope, Encoding.UTF8, "application/json");
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        HttpResponseMessage httpResponse = await client.PostAsync("", httpContent);
        if (httpResponse.Content != null)
        {
            string responseContent = await httpResponse.Content.ReadAsStringAsync();
            dynamic answer = JsonConvert.DeserializeObject(responseContent);
            summaries = answer[0].ToObject<List<String>>();
        }
    } 
}
Jean-Philippe
quelle
0

Versuchen Sie die Eigenschaft "Ergebnis"

class Program
{
    static void Main(string[] args)
    {
        test t = new test();
        t.Go().Result;
        Console.ReadKey();
    }
}
Chitta
quelle