Captura de valores de retorno de goroutines


Soy un novato en golang, así que por favor disculpe si esta es una pregunta muy básica. El siguiente código da error de compilación diciendo 'ir inesperado':

x := go doSomething(arg)

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

Lo sé, puedo obtener el valor devuelto 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.

Author: I159, 2014-01-06

3 answers

La respuesta estricta es que tú puedes hacer eso. Probablemente no sea una buena idea. Aquí está el 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? Es probable que quieras asegurarte de que el goroutine engendrado haya terminado con él para que no tengas una condición de raza. Pero si quieres hacer eso, necesitarás una forma de comunicarte con el goroutine, y si tienes una manera de hacerlo, ¿por qué no usarlo para devolver el valor?

 38
Author: joshlf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-13 03:24:23

¿Por qué no es posible obtener un valor devuelto de una goroutine asignándolo a una variable?

Ejecutar goroutine (asincrónicamente) y obtener el valor de retorno de la función son acciones esencialmente controvertidas. Cuando dices go quieres decir "hazlo asincrónicamente" o incluso más simple: "¡Adelante! No espere a que finalice la ejecución de la función". Pero cuando asigna el valor de retorno de la función a una variable, espera tener este valor dentro de la variable. Así que cuando haces eso x := go doSomething(arg) estás diciendo: "¡Vamos, no esperes a la función! ¡Espera, espera, espera! Necesito que un valor devuelto sea accesible en x var en la siguiente línea!"

Canales

La forma más natural de obtener un valor de una goroutine son los canales. Los canales son las tuberías que conectan goroutines concurrentes. Puede enviar valores a canales desde una goroutine y recibir esos valores en otra goroutine o en una función síncrona. Usted podría obtener fácilmente un valor de un goroutine no romper 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 está tomado de Go By Example

Paso de mensajes y CSP

Go se basa largamente en la teoría de CSP. La ingenua descripción de arriba podría describirse con precisión en términos de CSP (aunque creo que está fuera del alcance de la pregunta). Recomiendo encarecidamente que se familiarice con la teoría CSP, al menos porque es RAD. Estas citas cortas dan una dirección del pensamiento:

Como su nombre sugiere, CSP permite la descripción de sistemas en términos de procesos componentes que operan independientemente, e interactúan entre sí únicamente a través de comunicación de paso de mensajes.

En informática, el paso 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 ejecutarse. El paso de mensajes difiere de la programación convencional donde un proceso, subrutina o función es invocado directamente por su nombre.

 36
Author: I159,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-20 07:51:56

La idea de la palabra clave go es que ejecute la función doSomething de forma asíncrona, y continúe con la goroutine actual sin esperar el resultado, algo así como ejecutar un comando en un shell de Bash con un '&' después de él. Si quieres hacer

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

Entonces usted necesita el goroutine actual para bloquear hasta que doSomething termine. Entonces, ¿por qué no llamar a doSomething en el goroutine actual? Hay otras opciones (como, doSomething podría publicar un resultado en un canal, que el actual goroutine recibe valores de) pero simplemente llamar a doSomething y asignar el resultado a una variable es obviamente más simple.

 8
Author: MatrixFrog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-07 04:46:17