Poniższy kod powoduje błąd kompilacji z komunikatem „nieoczekiwane zakończenie”:
x := go doSomething(arg)
func doSomething(arg int) int{
...
return my_int_value
}
Wiem, że mogę pobrać wartość zwracaną, jeśli wywołam funkcję normalnie, bez użycia goroutine. Lub mogę użyć kanałów itp.
Moje pytanie brzmi: dlaczego nie można pobrać takiej wartości zwracanej z goroutine.
go
concurrency
goroutine
Nerw
źródło
źródło
Odpowiedzi:
Ścisła odpowiedź brzmi, że możesz to zrobić. To chyba nie jest dobry pomysł. Oto kod, który by to zrobił:
var x int go func() { x = doSomething() }()
Spowoduje to powstanie nowej gorutyny, która obliczy,
doSomething()
a następnie przypisze wynikx
. Problem w tym, że jak zamierzasz wykorzystaćx
oryginalną gorutynę? Prawdopodobnie chcesz się upewnić, że spawnowana gorutyna jest z nią zrobiona, abyś nie miał warunków rasy. Ale jeśli chcesz to zrobić, potrzebujesz sposobu na komunikację z gorutynem, a jeśli masz na to sposób, dlaczego nie użyć go po prostu do odesłania wartości?źródło
return
, toassign
mentDlaczego nie można pobrać wartości zwracanej z gorutyny przypisującej ją do zmiennej?
Uruchamianie goroutine (asynchronicznie) i pobieranie wartości zwracanej z funkcji są zasadniczo sprzecznymi akcjami. Kiedy mówisz, że
go
masz na myśli „rób to asynchronicznie” lub nawet prościej: „Dalej! Nie czekaj na zakończenie wykonywania funkcji”. Ale kiedy przypiszesz wartość zwracaną przez funkcję do zmiennej, spodziewasz się, że ta wartość będzie znajdować się w zmiennej. Więc kiedy to zrobisz,x := go doSomething(arg)
mówisz: "Dalej, nie czekaj na funkcję! Czekaj-czekaj-czekaj! Potrzebuję, aby zwrócona wartość była dostępna wx
var bezpośrednio w następnym wierszu poniżej!"Kanały
Najbardziej naturalnym sposobem uzyskania wartości z gorutyny są kanały. Kanały to rury, które łączą równoległe gorutyny. Możesz wysyłać wartości do kanałów z jednej gorutyny i odbierać te wartości do innej gorutyny lub w funkcji synchronicznej. Możesz łatwo uzyskać wartość z gorutyny, która nie przerywa współbieżności, używając
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) } } }
Przykład pochodzi z Go By Example
CSP i przekazywanie wiadomości
Go jest w dużej mierze oparty na teorii CSP . Naiwny opis z powyższego mógłby być precyzyjnie zarysowany w kontekście CSP (chociaż uważam, że nie wchodzi w zakres pytania). Zdecydowanie zalecam zapoznanie się z teorią CSP, przynajmniej dlatego, że jest to RAD. Te krótkie cytaty dają kierunek myślenia:
źródło
Idea
go
słowa kluczowego polega na tym, że uruchamiasz funkcję doSomething asynchronicznie i kontynuujesz bieżącą gorutynę bez czekania na wynik, podobnie jak wykonywanie polecenia w powłoce Bash ze znakiem „&” po nim. Jeśli chceszx := doSomething(arg) // Now do something with x
wtedy musisz zablokować bieżący goroutine, dopóki nie zakończy się funkcja doSomething. Dlaczego więc nie zadzwonić po prostu do funkcji doSomething w obecnym gorutynie? Istnieją inne opcje (na przykład doSomething może wysłać wynik do kanału, z którego bieżąca goroutine otrzymuje wartości), ale po prostu wywołanie funkcji doSomething i przypisanie wyniku do zmiennej jest oczywiście prostsze.
źródło
To wybór projektu przez twórców Go. Jest dużo abstrakcji / API do reprezentowania wartości asynchronicznych operacji I / O -
promise
,future
,async/await
,callback
,observable
, itd. Te abstrakcje / API są nieodłącznie związane z jednostką planowania - współprogram - a te abstrakcje / API dyktować jak współprogram ( a dokładniej wartości powrotu asynchroniczny I / o reprezentowane przez nie) może składać się .Go wybrał przekazywanie komunikatów (czyli kanały ) jako abstrakcję / API do reprezentowania wartości zwracanej przez asynchroniczne operacje we / wy. Oczywiście gorutyny i kanały zapewniają komponowalne narzędzie do implementacji asynchronicznych operacji we / wy.
źródło