Блокировка каналов (Channels)
Внимательные студенты, возможно, заметили, что при введении в объявление и инициализацию каналов (channels) мы не указывали емкость канала:
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)
}
Но при демонстрации операций с каналами мы указывали емкость канала:
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)
}
Для каналов, у которых емкость не указана, они называются небуферизованными каналами (unbuffered channels), у которых нет буферного пространства.
Если отправитель или получатель не готовы, первая операция будет заблокирована до тех пор, пока другая операция не станет готовой.
chan1 := make(chan int) // Unbuffered channel of type int
Для каналов, у которых указана емкость и есть буферное пространство, они называются буферизованными каналами (buffered channels).
chan2 := make(chan int, 5) // Buffered channel of type int with a capacity of 5
Они работают как очередь, следуя правилу "первым пришел - первым ушел" (First In First Out, FIFO).
Для буферизованных каналов мы можем использовать операции отправки для добавления элементов в конец очереди и операции приема для удаления элементов из начала очереди.
Что произойдет, если мы поместим в буферизованный канал больше данных, чем его емкость? Давайте проверим. Напишите следующий код в файл 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")
}
Запустите программу с помощью следующей команды:
go run channel1.go
Вывод программы будет следующим:
fatal error: all goroutines are asleep - deadlock!
Мы видим, что программа попадает в состояние блокировки (deadlock), потому что канал уже заполнен.
Чтобы решить эту проблему, мы можем сначала извлечь данные из канала. Напишите следующий код в файл 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")
}
Запустите программу с помощью следующей команды:
go run channel.go
Вывод программы будет следующим:
Data extracted: 10
succeed
Мы можем постоянно использовать канал с ограниченной емкостью, следуя стратегии "взять - использовать - взять - использовать снова".