golang concurrency go goroutine

concurrency - golang - ¿Cómo funcionan los goroutines?(o: goroutines y relación de hilos del sistema operativo)



go channels (4)

¿Cómo pueden seguir ejecutando otros goroutines mientras invocan un syscall? (cuando se usa GOMAXPROCS = 1)
Por lo que yo sé, al invocar un syscall el hilo pierde el control hasta que regrese el syscall. ¿Cómo puede Go lograr esta simultaneidad sin crear un hilo del sistema por bloqueo-en-syscall?

De la documentation :

Goroutines

Se llaman rutinas porque los términos existentes -hilos, correcciones, procesos, etc.- transmiten connotaciones inexactas. Un goroutine tiene un modelo simple: es una función que se ejecuta simultáneamente con otros goroutines en el mismo espacio de direcciones. Es liviano y cuesta poco más que la asignación de espacio de pila. Y los stacks comienzan siendo pequeños, por lo que son baratos y crecen asignando (y liberando) almacenamiento de almacenamiento dinámico según sea necesario.

Los goroutines se multiplexan en varios hilos del sistema operativo, por lo que si uno debe bloquearlos, como por ejemplo mientras espera la E / S, otros continuarán ejecutándose. Su diseño oculta muchas de las complejidades de la creación y administración de hilos.


Debe diferenciar el número de procesador y el número de subproceso: puede tener más subprocesos que procesadores físicos, por lo que un proceso de subprocesos múltiples puede ejecutarse en un único procesador central.

Como explica la documentación que citó, una rutina no es un hilo: es simplemente una función ejecutada en un hilo dedicado a un espacio de pila. Si su proceso tiene más de un hilo, esta función puede ejecutarse por cualquiera de los hilos. De modo que una rutina que está bloqueando por una razón u otra (syscall, E / S, sincronización) puede dejarse en su hilo mientras que otras pueden ser ejecutadas por otra.


No puede. Solo hay 1 goroutine que se puede ejecutar a la vez cuando GOMAXPROCS = 1, ya sea que uno de ellos haga una llamada al sistema u otra cosa.

Sin embargo, la mayoría de las llamadas al sistema de bloqueo, como E / S de socket, que esperan un temporizador no se bloquean en una llamada al sistema cuando se realizan desde Go. Son multiplexados por el tiempo de ejecución Go en epoll, kqueue o instalaciones similares que el sistema operativo proporciona para la multiplexación de E / S.

Para otros tipos de llamadas al sistema de bloqueo que no se pueden multiplexar con algo como epoll, Go genera un nuevo hilo del sistema operativo, independientemente de la configuración de GOMAXPROCS (aunque ese era el estado en Go 1.1, no estoy seguro de si la situación se ha modificado)


Ok, entonces esto es lo que aprendí: cuando realizas syscalls sin procesar, Go crea un hilo por goroutine de bloqueo. Por ejemplo, considere el siguiente código:

package main import ( "fmt" "syscall" ) func block(c chan bool) { fmt.Println("block() enter") buf := make([]byte, 1024) _, _ = syscall.Read(0, buf) // block on doing an unbuffered read on STDIN fmt.Println("block() exit") c <- true // main() we''re done } func main() { c := make(chan bool) for i := 0; i < 1000; i++ { go block(c) } for i := 0; i < 1000; i++ { _ = <-c } }

Al ejecutarlo, Ubuntu 12.04 reportó 1004 hilos para ese proceso.

Por otro lado, al utilizar el servidor HTTP de Go y abrirle 1000 sockets, solo se crearon 4 subprocesos del sistema operativo:

package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:]) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }

Entonces, es una mezcla entre un IOLoop y un hilo por llamada al sistema de bloqueo.