2012-03-28 11 views
11

C# haben eine coole neue FunktionUnterschiede zwischen C# async und Java ExecutorService

public Task<string> async f() 
{ 
    string r = LongCompute(); 
    return r; 
} 

aber ist das nicht gleichbedeutend mit

public Future<String> f() { 
    return Globals.executorService.submit(new Callable<String>() { 
     public String call() throws Exception { 
      String r = longCompute(); 
      return r; 
     } 
    }); 
} 

wo in Java Sie mehr Flexibilität haben den Threadpool zu wählen, in dem Die Aufgabe würde ausgeführt werden.

Was erwarten über? Es ist gleichbedeutend mit nur erhalten Aufruf

string s = await f(); 

ist wie

String s = f().get(); 

Gibt es etwas zu C#, oder ist es in der Tat nur ein syntaktischer Zucker in der Java-Version? (Ich bin kein C# Guru, also könnte ich etwas verpassen).

+0

Meinten Sie 'string r = erwarten LongCompute();'? Wenn nicht, dann ist Ihre Methode überhaupt nicht asynchron (und der Compiler wird Ihnen dies in Form einer Warnung mitteilen). – svick

+0

@svick der Methode Körper wird nicht, aber der Aufrufer der Methode wird. –

+0

Nein, wird es nicht. Wenn Sie 'f' (f ')' aufrufen, wird es genau so umgeschrieben, wie Jon in seiner Antwort erklärt hat, aber es wird immer noch vollständig synchron laufen. Das liegt daran, dass das Ausführen von Dingen asynchron einen gewissen Overhead verursacht und das Framework versucht, es nach Möglichkeit zu vermeiden. So läuft zum Beispiel jeder Code in einer 'async'-Methode vor dem ersten' '' '' synchron. – svick

Antwort

28

Nein, await ist nicht wie nur get() anrufen. Es gibt wesentlich mehr dazu.

Wenn Sie einen Ausdruck await in C# verwenden, erstellt der Compiler effektiv eine Fortsetzung, so dass die Methode sofort zurückkehren und die Verarbeitung nur fortsetzen kann, wenn das Erwartete noch nicht abgeschlossen ist. Die Fortsetzung wird in einem entsprechenden Kontext ausgeführt - also, wenn Sie auf einem UI-Thread vor dem await Ausdruck sind, werden Sie danach auf dem UI-Thread fortsetzen werden, ohne jedoch den UI-Thread blockiert, während Sie für das Ergebnis auf jemand warten. Zum Beispiel:

public async void HandleButtonClick(object sender, EventArgs e) 
{ 
    // All of this method will run in the UI thread, which it needs 
    // to as it touches the UI... however, it won't block when it does 
    // the web operation. 

    string url = urlTextBox.Text; 
    WebClient client = new WebClient(); 
    string webText = await client.DownloadStringTaskAsync(url); 

    // Continuation... automatically called in the UI thread, with appropriate 
    // context (local variables etc) which we used earlier. 
    sizeTextBox.Text = string.Format("{0}: {1}", url, webText.Length); 
} 

Letztlich ist es alles syntaktischen Zucker, aber viel kompliziertere Zucker als das, was Sie gezeigt haben.

Es gibt eine Menge von Informationen im Internet verfügbar bereits. Zum Beispiel:

+1

Aber wenn es dir egal ist, welcher Thread was läuft, ist es nicht gleichwertig. Die Fortsetzung in Zeile 12 entspricht dem Blockieren eines Threads in Zeile 12, oder?Der einzige Vorteil besteht darin, dass alles auf demselben Thread ausgeführt wird, was normalerweise nur nützlich ist, weil der UI-Thread privilegiert ist. Hab ich recht? –

+1

@ Chi-Lan: Nicht wirklich. Angenommen, Sie haben einen Server, der eine lange Abfrage durchführen soll, also möglicherweise Hunderttausende aktive Verbindungen hat. An diesem Punkt wollen Sie * das * Threat-pro-Request-Modell wirklich nicht, also können Sie das Warten auf "was auch immer eine Antwort auslöst" nicht blockieren. Sie müssen also * irgendeine * Art von Fortsetzungsmechanismus haben - und die Sprachunterstützung in C# 5 macht das wirklich einfach; Sie können Code schreiben, der aussieht und sich anfühlt wie der synchrone Code, den wir relativ gut schreiben können, aber mit asynchronen Eigenschaften. –

+1

hmmm ... hab dich. Es entspricht dem Modell mit leichten/grünen Fäden, das wir in letzter Zeit gesehen haben, zB golang.org mit der Möglichkeit, den richtigen Faden zu markieren, an dem Sie Ihre Coroutine ausführen möchten. Bin ich jetzt richtig? –

7

Jon nicht erklären, den eigentlichen Punkt.

Java ExecutorService basiert auf Fäden, während C# 's await kann gesagt werden, werden basierend auf Fasern.

Beide ermöglichen Multitasking, bei dem Rechenressourcen zwischen gleichzeitigen Funktionen aufgeteilt werden (dh Funktionen, die "gleichzeitig" ausgeführt werden). Die erste Art von Multitasking heißt Präventiv, während die zweite kooperative. In der Vergangenheit wurde präemptives Multitasking als fortschrittlicher und kooperativer angesehen. In der Tat, bevor präemptives Multitasking von den Betriebssystemen der Verbraucher unterstützt wurde, haben Computer wirklich gesaugt. Preemptives Multitasking hat jedoch seine Nachteile. Es kann schwer zu programmieren sein und es benötigt mehr Speicher. Der Hauptunterschied zwischen den beiden ist, dass präemptives Multitasking es der Laufzeit (normalerweise dem Betriebssystem selbst) ermöglicht, jede Funktion zu jeder Zeit zu stoppen und eine andere Funktion zu starten (und sie gleichzeitig auf verschiedenen CPUs auszuführen). In der Zwischenzeit erfordert kooperatives Multitasking, dass die Running-Funktion beendet oder freiwillig angehalten wird. Die meisten von uns kennen präemptives Multitasking in Form von Multithreading sowie die damit verbundene sorgfältige Programmierung. Weniger bekannt ist das kooperative Multitasking, das heutzutage oft als Fasern oder Koroutinen bezeichnet wird (in diesem Fall ist es im Benutzerland innerhalb der Threads eines präventiven Betriebssystems implementiert).

Auf jeden Fall ist der Punkt, dass ExecutorService und await nicht direkt vergleichbar ist, und await ist nicht im allgemeinen überlegen echten Multithreading (außer, dass es schön syntaktischen Zucker kennzeichnet). C# 's Grund für die Einbeziehung await (und basierend auf kooperativen Multitasking) ist, dass die wichtigsten GUI-Toolkits auf der Plattform nicht für Multithreading ausgelegt sind und das Refactoring zur Unterstützung der Parallelität eine Menge Arbeit kosten würde. Kooperatives Multitasking funktioniert gut für die Benutzeroberfläche, da die meisten Ereignisbehandlungsroutinen kurz sind und seriell ausgeführt werden können. await erweitert das Konzept der Ereignisschleife, indem lange Event-Handler sich selbst pausieren lassen und fortsetzen, nachdem die Rendering-Funktion eine Chance zum Ausführen hat. All dies geschieht in einem einzigen Thread auf einem einzelnen CPU-Kern.

Wo beide finden gemeinsame Grund ist, dass sie beide Formen von Multitasking sind, und Future.get und await Task sind beide Formen der Synchronisation.

Wie erwartet, ist C# nicht ohne gute Unterstützung für Threads und Thread-Pools. Ebenso enthält Java Fasern/Co-Routinen/Async in vielen Bibliotheken, wie Servlet 3.0 und javafx.concurrent.Task.

Als Reaktion auf Jon Skeet: Fortsetzung (wie der Benutzerland-Implementierungsmechanismus von Fasern genannt wird) ist nicht trivial, aber Threads sind nicht weniger anspruchsvoll in ihrer Implementierung. Jon wurde vielleicht abgeworfen, weil die Algorithmen hinter Threads im Betriebssystem und nicht im Compiler oder in der .NET-Laufzeitumgebung liegen.

Verwandte Themen