2016-05-12 7 views
2

Ich wünschte, ich so etwas wie der folgende Code mit golang tun könnte:Golang goroutine kann nicht Funktion Rückgabewert (e) verwenden

package main 

import (
    "fmt" 
    "time" 
) 

func getA() (int) { 

    fmt.Println("getA: Calculating...") 
    time.Sleep(300 * time.Millisecond) 

    fmt.Println("getA: Done!") 
    return 100 
} 

func getB() (int) { 

    fmt.Println("getB: Calculating...") 
    time.Sleep(400 * time.Millisecond) 

    fmt.Println("getB: Done!") 
    return 200 
} 

func main() { 

    A := go getA() 
    B := go getB() 

    C := A + B // waits till getA() and getB() return 
    fmt.Println("Result:", C) 
    fmt.Println("All done!") 
} 

Genauer gesagt, ich wünsche Go Gleichzeitigkeit hinter der Szene umgehen konnte.

Das könnte ein bisschen off topic sein, aber ich bin gespannt, was Leute denken, solche implizite Nebenläufigkeit Unterstützung zu haben. Lohnt es sich, etwas dafür zu tun? und was sind die möglichen Schwierigkeiten und Nachteile?


Edit:

Um klar zu sein, die Frage ist nicht über „? Was Go ist jetzt zu tun“, und nicht einmal „? Wie es umgesetzt wird“ obwohl ich @icza Post zu schätzen weiß, was genau wir von Go erwarten sollten, wie es jetzt ist. Die Frage ist, warum es keine Werte zurückgeben kann oder nicht, und welche potenziellen Komplikationen ergeben sich daraus?

Zurück zu meinem einfaches Beispiel:

A := go getA() 
    B := go getB() 

    C := A + B // waits till getA() and getB() return 

Ich sehe keine Probleme, den Umfang der Variablen in Bezug auf zumindest aus der Syntax Sicht. Der Gültigkeitsbereich von A, B und C wird klar durch den Block definiert, in dem sie sich befinden (in meinem Beispiel ist der Bereich die main()-Funktion). Eine vielleicht berechtigtere Frage wäre jedoch, ob diese Variablen (hier A und B) "bereit" zum Lesen sind? Natürlich sollten sie nicht bereit und zugänglich sein, bis und getB() fertig sind. Genau das ist alles, wonach ich fragen möchte: Der Compiler könnte alle Glocken und Pfeifen hinter der Szene implementieren, um sicherzustellen, dass die Ausführung blockiert wird, bis A und B bereit sind zu konsumieren (anstatt den Programmierer zu zwingen, diese Wartezeiten explizit zu implementieren) und pfeift mit Kanälen).

Dies könnte die gleichzeitige Programmierung viel einfacher machen, insbesondere für Fälle, in denen Rechenaufgaben voneinander unabhängig sind. Die Kanäle könnten weiterhin für eine explizite Kommunikation und Synchronisation verwendet werden, falls dies wirklich benötigt wird.

+1

Related: [Was passiert mit Wert von goroutine zurückgeben] (http://stackoverflow.com/questions/27868369/what-happens-to-return-value-from-goriginute) – icza

+0

Dies hinter der Szene ist zu viel Abstraktion. Goroutines sind verschiedene laufende Prozesse oder Threads, und soweit ich weiß, gibt es keine Möglichkeit (oder zumindest nicht empfohlen), Ergebnisse von zwei Prozessen oder Threads zu verbinden, ohne explizit den Join aufzurufen und den Wert abzurufen. – PieOhPah

Antwort

10

Aber das ist sehr einfach und idiomatisch machbar. Die Sprache bietet die Mittel: Channel types.

Einfach einen Kanal an die Funktionen übergeben, und die Funktionen senden das Ergebnis auf dem Kanal, anstatt sie zurückzugeben. Kanäle sind sicher für die gleichzeitige Verwendung.

Nur eine Goroutine hat zu jeder Zeit Zugriff auf den Wert. Datenrennen können nicht auftreten.

Für mehr, überprüfen Sie die Frage aus: If I am using channels properly should I need to use mutexes?

Beispiellösung:

func getA(ch chan int) { 
    fmt.Println("getA: Calculating...") 
    time.Sleep(300 * time.Millisecond) 

    fmt.Println("getA: Done!") 
    ch <- 100 
} 

func getB(ch chan int) { 
    fmt.Println("getB: Calculating...") 
    time.Sleep(400 * time.Millisecond) 

    fmt.Println("getB: Done!") 
    ch <- 200 
} 

func main() { 
    cha := make(chan int) 
    chb := make(chan int) 

    go getA(cha) 
    go getB(chb) 

    C := <-cha + <-chb // waits till getA() and getB() return 
    fmt.Println("Result:", C) 
    fmt.Println("All done!") 
} 

Output (versuchen Sie es auf dem Go Playground):

getB: Calculating... 
getA: Calculating... 
getA: Done! 
getB: Done! 
Result: 300 
All done! 

Hinweis:

Das obige Beispiel kann auch mit einem einzigen Kanal durchgeführt werden:

func main() { 
    ch := make(chan int) 

    go getA(ch) 
    go getB(ch) 

    C := <-ch + <-ch // waits till getA() and getB() return 
    fmt.Println("Result:", C) 
    fmt.Println("All done!") 
} 

Ausgang das gleiche ist. Versuchen Sie diese Variante auf der Go Playground.


Edit:

Die Go spec states, dass die Rückgabewerte solcher Funktionen werden verworfen. Mehr dazu: What happens to return value from goroutine.

Was Sie vorschlagen, blutet aus mehreren Wunden. Jede Variable in Go hat eine scope (auf die sie bezogen werden kann). Der Zugriff auf Variablen wird nicht blockiert. Die Ausführung von Anweisungen oder Operatoren kann blockieren (z. B. Receive operator oder Send statement).

Ihr Vorschlag:

go A := getA() 
go B := getB() 

C := A + B // waits till getA() and getB() return 

Was ist der Umfang der A und B? 2 vernünftige Antwort wäre a) aus der go Erklärung oder nach der go Aussage. In jedem Fall sollten wir nach der go Anweisung darauf zugreifen können. Nach der go-Anweisung wären sie im Geltungsbereich, das Lesen/Schreiben ihrer Werte sollte nicht blockiert werden.

Aber dann, wenn C := A + B blockieren würde nicht (weil es einfach ist, eine Variable zu lesen), dann entweder

a) in dieser Zeile A und B bereits bestückt werden sollen, die die go Aussage bedeutet, würde für getA() warten müssen zu vervollständigen (aber dann schlägt es den Zweck von go Aussage)

b) oder sonst würden wir etwas externen Code benötigen, um zu synchronisieren, aber dann wieder gewinnen wir nichts (machen Sie es nur schlechter im Vergleich zur Lösung mit Kanälen) .

Do not communicate by sharing memory; instead, share memory by communicating.

von Kanälen, ist es kristallklar, was (Mai) Block und was nicht. Es ist glasklar, dass wenn ein Empfang von dem Kanal abgeschlossen ist, die Goroutine fertig ist. Und es gibt uns das Mittel, den Empfang auszuführen, wann immer wir wollen (an dem Punkt, an dem sein Wert benötigt wird und wir bereit sind, darauf zu warten), und auch das Mittel, um zu prüfen, ob der Wert ohne Blockierung bereit ist. Komma-ok Idiom).

+0

Danke @icza. Ja, es ist machbar, und genau das machen wir gerade jetzt. Aber abgesehen von der Bequemlichkeit ist es eher so, wie es auf traditionelle Weise gemacht wird. Wenn Sie bereits über eine Funktion verfügen, die implementiert wird, um einen Wert zurückzugeben, müssen Sie entweder die Funktion ändern oder sie in eine andere Funktion umbrechen, um auf die Ausgabe zugreifen zu können. – hagh

+0

Darüber hinaus ist dies syntaktisch (und vielleicht sogar semantisch) inkonsistent mit der Syntax der allgemeinen Funktionsdeklaration und ihrer Bedeutung in der Go-Sprache. – hagh

+0

@ user3324751 Einige Änderungen hinzugefügt, Ihre Fragen beantwortet? – icza