Rückholwerte von Goroutinen abfangen


81

Der folgende Code gibt einen Kompilierungsfehler mit der Meldung "Unerwartetes Los" aus:

x := go doSomething(arg)

func doSomething(arg int) int{
    ...
    return my_int_value
}

Ich weiß, ich kann den Rückgabewert abrufen, wenn ich die Funktion normal aufrufe, ohne Goroutine zu verwenden. Oder ich kann Kanäle usw. verwenden.

Meine Frage ist, warum es nicht möglich ist, einen solchen Rückgabewert von einer Goroutine abzurufen.


7
Sie könnten einen Kanal verwenden, um es zurückzugeben
Rogerdpack

Warum erlaubt es einen Rückgabewert für eine Goroutine
Srinath Samala

1
@rogerdpack, bei dem die API der von Ihnen verwendeten Funktion geändert werden muss. Wenn Sie nicht Ihre eigene sind, benötigen Sie möglicherweise eine Wrapper-Funktion
David Callanan,

Antworten:


66

Die strenge Antwort ist, dass Sie das tun können . Es ist einfach wahrscheinlich keine gute Idee. Hier ist Code, der das tun würde:

var x int
go func() {
    x = doSomething()
}()

Dadurch wird eine neue Goroutine erzeugt, die doSomething()das Ergebnis berechnet und dann zuweist x. Das Problem ist: Wie werden Sie xvon der ursprünglichen Goroutine verwenden? Sie möchten wahrscheinlich sicherstellen, dass die gespawnte Goroutine damit fertig ist, damit Sie keine Rennbedingung haben. Aber wenn Sie das tun möchten, brauchen Sie eine Möglichkeit, mit der Goroutine zu kommunizieren, und wenn Sie eine Möglichkeit haben, dies zu tun, warum nicht einfach verwenden, um den Wert zurückzusenden?


6
Sie können eine WaitGroup hinzufügen, um sicherzustellen, dass Sie fertig sind, und darauf warten. Aber wie Sie sagten, es ist einfach nicht der richtige Weg, es ist ein Kanal.
Not_a_Golfer

1
Dies ist nicht return, dies ist assignment
Nidhin David

92

Warum ist es nicht möglich, einen Rückgabewert von einer Goroutine abzurufen, die ihn einer Variablen zuweist?

Goroutine ausführen (asynchron) und Rückgabewert von Funktion abrufen sind im Wesentlichen widersprüchliche Aktionen. Wenn Sie sagen, goSie meinen "asynchron machen" oder noch einfacher: "Weiter! Warten Sie nicht, bis die Funktionsausführung abgeschlossen ist". Wenn Sie jedoch einer Variablen einen Funktionsrückgabewert zuweisen, erwarten Sie, dass dieser Wert in der Variablen enthalten ist. Wenn Sie das tun, x := go doSomething(arg)sagen Sie: "Weiter, warten Sie nicht auf die Funktion! Warten-warten-warten! Ich brauche einen zurückgegebenen Wert, der in xvar direkt in der nächsten Zeile unten verfügbar ist!"

Kanäle

Der natürlichste Weg, einen Wert von einer Goroutine abzurufen, sind Kanäle. Kanäle sind die Rohre, die gleichzeitige Goroutinen verbinden. Sie können Werte von einer Goroutine in Kanäle senden und diese Werte in eine andere Goroutine oder in einer synchronen Funktion empfangen. Sie können leicht einen Wert von einer Goroutine erhalten, die die Parallelität nicht unterbricht, indem Sie select:

func main() {

    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        // Await both of these values
        // simultaneously, printing each one as it arrives.
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        } 
    }
}

Das Beispiel stammt aus Go By Example

CSP & Message-Passing

Go basiert größtenteils auf der CSP-Theorie . Die naive Beschreibung von oben könnte in Bezug auf CSP genau umrissen werden (obwohl ich glaube, dass dies nicht in Frage kommt). Ich empfehle dringend, sich mit der CSP-Theorie vertraut zu machen, zumindest weil es sich um RAD handelt. Diese kurzen Zitate geben eine Denkrichtung vor:

Wie der Name schon sagt, ermöglicht CSP die Beschreibung von Systemen anhand von Komponentenprozessen, die unabhängig voneinander arbeiten und ausschließlich durch Kommunikation über die Weitergabe von Nachrichten miteinander interagieren .

In der Informatik sendet die Nachrichtenübermittlung eine Nachricht an einen Prozess und stützt sich auf den Prozess und die unterstützende Infrastruktur, um den tatsächlich auszuführenden Code auszuwählen und aufzurufen. Das Weiterleiten von Nachrichten unterscheidet sich von der herkömmlichen Programmierung, bei der ein Prozess, eine Unterroutine oder eine Funktion direkt mit Namen aufgerufen wird.


9

Die Idee des goSchlüsselworts ist, dass Sie die Funktion doSomething asynchron ausführen und die aktuelle Goroutine fortsetzen, ohne auf das Ergebnis zu warten, ähnlich wie beim Ausführen eines Befehls in einer Bash-Shell mit einem '&' danach. Wenn du willst

x := doSomething(arg)
// Now do something with x

Dann muss die aktuelle Goroutine blockiert werden, bis doSomething beendet ist. Warum also nicht einfach doSomething in der aktuellen Goroutine aufrufen? Es gibt andere Optionen (z. B. könnte doSomething ein Ergebnis an einen Kanal senden, von dem die aktuelle Goroutine Werte empfängt), aber es ist offensichtlich einfacher, einfach doSomething aufzurufen und das Ergebnis einer Variablen zuzuweisen.


0

Es ist eine Design-Wahl von Go-Machern. Es gibt eine ganze Menge von Abstraktionen / APIs , den Wert darzustellen async I / O - Operationen - promise, future, async/await, callback, observableusw. Diese Abstraktionen / APIs sind von Natur gebunden an die Einheit der Terminplanung - Koroutinen - und diese Abstraktionen / APIs diktieren , wie Koroutinen ( oder genauer gesagt, der Rückgabewert der von ihnen dargestellten asynchronen E / A kann zusammengesetzt werden .

Go wählte Message Passing (auch bekannt als Channels ) als Abstraktion / API, um den Rückgabewert von asynchronen E / A-Operationen darzustellen. Goroutinen und Kanäle bieten Ihnen natürlich ein zusammensetzbares Tool zum Implementieren asynchroner E / A-Operationen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.