Bloqueo de Canales (Channels)
Los estudiantes atentos pueden haber notado que cuando introdujimos las declaraciones e inicialización de canales (channels), no especificamos la capacidad del canal:
package main
import "fmt"
func main() {
// Stores integer data in the channel
ch1 := make(chan int) // Same below
// Stores boolean data in the channel
ch2 := make(chan bool)
// Stores []int data in the channel
ch3 := make(chan []int)
fmt.Println(ch1, ch2, ch3)
}
Pero cuando demostramos las operaciones de canales, especificamos la capacidad del canal:
package main
import "fmt"
func main() {
// Specify the channel capacity as 3
ch := make(chan int, 3)
ch <- 10
ch <- 20
fmt.Println(<-ch)
fmt.Println(<-ch)
}
Para los canales sin capacidad especificada, se les llama canales sin búfer (unbuffered channels), que no tienen espacio de búfer.
Si un emisor o receptor no está listo, la primera operación se bloqueará hasta que la otra operación esté lista.
chan1 := make(chan int) // Unbuffered channel of type int
Para los canales con capacidad y espacio de búfer especificados, se les llama canales con búfer (buffered channels).
chan2 := make(chan int, 5) // Buffered channel of type int with a capacity of 5
Funcionan como una cola, siguiendo la regla de Primero en Entrar, Primero en Salir (First In First Out, FIFO).
Para los canales con búfer, podemos usar operaciones de envío para agregar elementos al final de la cola y usar operaciones de recepción para eliminar elementos del frente de la cola.
¿Qué sucede si ponemos más datos en un canal con búfer que su capacidad? Vamos a comprobarlo. Escribe el siguiente código en el archivo channel1.go
:
cd ~/project
touch channel1.go
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 10
ch <- 20
fmt.Println("succeed")
}
Ejecuta el programa utilizando el siguiente comando:
go run channel1.go
La salida del programa es la siguiente:
fatal error: all goroutines are asleep - deadlock!
Descubrimos que el programa entra en un bloqueo (deadlock) porque el canal ya está lleno.
Para resolver este problema, podemos extraer los datos del canal primero. Escribe el siguiente código en channel.go
:
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 10
fmt.Println("Data extracted:", <-ch)
ch <- 20
fmt.Println("succeed")
}
Ejecuta el programa utilizando el siguiente comando:
go run channel.go
La salida del programa es la siguiente:
Data extracted: 10
succeed
Podemos usar continuamente el canal con una capacidad limitada adoptando una estrategia de tomar, usar, tomar y usar de nuevo.