for - golang concurrency
¿Cómo se comporta make(chan bool) de forma diferente a make(chan bool, 1)? (3)
¿Cuál es el punto de un canal que puede caber 0 valores en él
Primero quiero señalar que el segundo parámetro aquí significa el tamaño del búfer, por lo que es simplemente un canal sin búferes (canal sin búfer).
En realidad esa es la razón por la cual se genera su problema. Los canales sin búfer solo se pueden escribir cuando hay alguien bloqueando para leer, lo que significa que debe tener algunas corutinas para trabajar, en lugar de esta sola.
Ver también The Go Memory Model :
Una recepción desde un canal sin búfer ocurre antes de que se complete el envío en ese canal.
Mi pregunta surge al intentar leer un canal, si puedo, o escribirlo, si puedo, usando una declaración de select
.
Sé que los canales especificados como make(chan bool, 1)
están almacenados en el búfer, y parte de mi pregunta es cuál es la diferencia entre eso y make(chan bool)
, que esta página dice que es lo mismo que make(chan bool, 0)
--- ¿Cuál es el punto de un canal que puede caber 0 valores en él?
Ver el patio A :
chanFoo := make(chan bool)
for i := 0; i < 5; i++ {
select {
case <-chanFoo:
fmt.Println("Read")
case chanFoo <- true:
fmt.Println("Write")
default:
fmt.Println("Neither")
}
}
Una salida:
Neither
Neither
Neither
Neither
Neither
(¡Eliminar el caso default
da como resultado un punto muerto!)
Ahora mira el patio B :
chanFoo := make(chan bool, 1) // the only difference is the buffer size of 1
for i := 0; i < 5; i++ {
select {
case <-chanFoo:
fmt.Println("Read")
case chanFoo <- true:
fmt.Println("Write")
default:
fmt.Println("Neither")
}
}
Salida B:
Write
Read
Write
Read
Write
En mi caso, el resultado B es lo que quiero. ¿De qué sirven los canales sin búfer? Todos los ejemplos que veo en golang.org parecen usarlos para enviar una señal / valor a la vez (que es todo lo que necesito), pero como en el patio A, el canal nunca se lee ni escribe. ¿Qué me falta aquí en mi comprensión de los canales?
Los canales sin búfer (creados sin capacidad) bloquearán el emisor hasta que alguien pueda leer de ellos, por lo que para que funcione como espera, debe usar dos goroutines para evitar el punto muerto en el mismo hilo.
Por ejemplo, con este código: http://play.golang.org/p/KWJ1gbdSqf
También incluye un mecanismo para que el func principal detecte cuando ambos goroutines han terminado.
package main
import "fmt"
func send(out, finish chan bool) {
for i := 0; i < 5; i++ {
out <- true
fmt.Println("Write")
}
finish <- true
close(out)
}
func recv(in, finish chan bool) {
for _ = range in {
fmt.Println("Read")
}
finish <- true
}
func main() {
chanFoo := make(chan bool)
chanfinish := make(chan bool)
go send(chanFoo, chanfinish)
go recv(chanFoo, chanfinish)
<-chanfinish
<-chanfinish
}
No mostrará la Lectura y Escritura alternativas, ya que tan pronto como se send
escrituras al canal, se bloquea, antes de que pueda imprimir la "Escritura", por lo que la ejecución se mueve a recv
que recibe el canal e imprime "Leer". Intentará leer nuevamente el canal, pero se bloqueará y la ejecución se moverá para send
. Ahora send
puede escribir el primer "Escribir", enviar al canal (sin bloquear porque ahora hay un receptor listo) y escribir el segundo "Escribir". En cualquier caso, esto no es determinista y el planificador puede mover la ejecución en cualquier punto a cualquier otra rutina de ejecución (al menos en la última versión 1.2).
Un canal sin búferes significa que las operaciones de lectura y escritura desde y hacia el canal son bloqueadas.
En una declaración select
:
- la lectura funcionaría si alguna otra rutina estuviera actualmente bloqueada por escrito en el canal
- la escritura funcionaría si alguna otra rutina estuviera actualmente bloqueada en la lectura del canal
- de lo contrario, se ejecuta el caso
default
, que ocurre en su caso A.