usar - Captura de valores de retorno de los goroutinos
retornar valor en c (3)
¿Por qué no es posible obtener un valor de retorno de un goroutine asignándolo a una variable?
Ejecutar goroutine (asincrónicamente) y recuperar el valor de retorno de la función son acciones esencialmente controvertidas. Cuando dice go
quiere decir "hágalo de forma asíncrona" o incluso más simple: "¡Continúe! No espere a que se complete la ejecución de la función". Pero cuando asigna un valor de retorno de función a una variable, espera tener este valor dentro de la variable. Entonces, cuando haces eso x := go doSomething(arg)
estás diciendo: "¡Adelante, no esperes la función! Espera, espera, espera! Necesito que se pueda acceder a un valor devuelto en x
var, en la siguiente línea ¡abajo!"
Canales
La forma más natural de obtener un valor de un goroutine es canales. Los canales son los conductos que conectan los goroutines concurrentes. Puede enviar valores a los canales de un administrador de rutina y recibir esos valores en otra rutina o en una función síncrona. Puede obtener fácilmente un valor de un goroutine que no rompa la concurrencia usando 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)
}
}
}
El ejemplo se toma de Go By Example
CSP y transmisión de mensajes
Go está más basado en la teoría CSP . La descripción ingenua de arriba podría ser delineada con precisión en términos de CSP (aunque creo que está fuera del alcance de la pregunta). Recomiendo encarecidamente familiarizarse con la teoría CSP al menos porque es RAD. Estas citas cortas dan una dirección de pensamiento:
Como su nombre lo sugiere, CSP permite la descripción de sistemas en términos de procesos de componentes que operan de forma independiente , e interactúan entre sí únicamente a través de la comunicación de paso de mensajes .
En informática, el envío de mensajes envía un mensaje a un proceso y se basa en el proceso y la infraestructura de soporte para seleccionar e invocar el código real para ejecutar. La transferencia de mensajes difiere de la programación convencional en que un proceso, subrutina o función se invoca directamente por su nombre.
Soy un novato en Golang, así que disculpe si esta es una pregunta muy básica. El código siguiente muestra un error de compilación que dice ''inesperado'':
x := go doSomething(arg)
func doSomething(arg int) int{
...
return my_int_value
}
Lo sé, puedo recuperar el valor de retorno si llamo a la función normalmente, sin usar goroutine. O puedo usar canales, etc.
Mi pregunta es por qué no es posible obtener un valor de retorno como este de un goroutine.
La idea de la palabra clave go
es que ejecute la función doSomething de forma asíncrona, y continúe la rutina actual sin esperar el resultado, como ejecutar un comando en un shell Bash con un ''&'' después de él. Si quieres hacer
x := doSomething(arg)
// Now do something with x
entonces necesitas la rutina de goroutine para bloquear hasta que termine algo. Entonces, ¿por qué no simplemente llamar a doSomething en el goroutine actual? Hay otras opciones (como, doSomething podría publicar un resultado en un canal, del cual la rutina actual recibe valores) pero simplemente llamar a DoSomething y asignar el resultado a una variable es obviamente más simple.
La respuesta estricta es que puedes hacer eso. Probablemente no sea una buena idea. Aquí hay un código que haría eso:
var x int
go func() {
x = doSomething()
}()
Esto generará una nueva goroutine que calculará doSomething()
y luego asignará el resultado a x
. El problema es: ¿cómo vas a usar x
del goroutine original? Probablemente quieras asegurarte de que la goroutine engendrada haya finalizado para que no tengas una condición de carrera. Pero si quieres hacerlo, necesitarás una forma de comunicarte con la rutina, y si tienes una forma de hacerlo, ¿por qué no utilizarlo para devolver el valor?