Тип канала является родным для Go, что эквивалентно очереди «первым поступил — первым обслужен» и является единственным типом, который может обеспечить безопасность параллелизма. При объявлении переменной типа канала сначала нужно определить тип элемента типа канала, а затем определить пропускную способность канала.Разумеется, пропускная способность по умолчанию равна 0.
инициализация
Используйте make для инициализации следующим образом:
c := make(chan int)
c := make(chan string, 10)
Если пропускная способность не указана, пропускная способность канала по умолчанию равна 0, и этот канал также становится небуферизованным.
Характеристики передачи и приема канала
-
Для одного и того же канала операции отправки являются взаимоисключающими, и операции приема также являются взаимоисключающими.
Проще говоря, в то же время система выполнения Go будет выполнять только одну из любых операций отправки в тот же канал, а другие операции отправки не будут выполняться до тех пор, пока значение элемента не будет полностью скопировано в канал. То же самое относится и к операциям приема. Операции отправки и получения также являются взаимоисключающими для одного и того же значения в канале. Если значение элемента копируется в канал, но не было скопировано, получатель его не увидит и не примет.
подсказки Значения элементов, поступающие в канал из внешнего мира, копируются. То есть в канал поступает не значение элемента в правой части оператора приема, а его копия.
-
Обработка значений элементов как в операциях отправки, так и в операциях получения неразделима. Неделимый означает, что операция отправки либо не скопировала элемент, либо уже скопировала его, и не бывает ситуации, когда копируется только часть значения.
-
Операция отправки блокируется до тех пор, пока не будет полностью завершена. То же самое относится и к операциям приема. Операция отправки включает два шага: «копировать значение элемента» и «поместить копию в канал». Пока эти два шага не будут выполнены, операция отправки будет заблокирована, а код после него выполняться не будет. Операция приема включает в себя три операции: «скопировать значение элемента в канал», «поместить копию в приемник» и «удалить исходное значение». Эти три операции также будут заблокированы до завершения.
Советы. Копия, упомянутая выше, является поверхностной копией. Поверхностная копия просто копирует значение и то, что непосредственно содержится в значении. Глубокая копия — это копирование всех глубоких структур вместе. В Golang есть только поверхностная копия.
Когда операции отправки и получения будут заблокированы?
для кэш-канала
- Если канал заполнен, все операции отправки будут заблокированы до тех пор, пока элемент не будет удален из канала.
- Если канал пуст, все операции приема будут заблокированы до тех пор, пока в канале не появится новый элемент.
Для небуферизованных каналов
- Либо операция отправки, либо операция получения блокируются в начале, и только парная операция начнет выполняться.
Когда операции отправки и получения вызовут панику
-
Канал закрыт, а операция отправки вызовет панику
-
Закрытие уже закрытого канала тоже вызывает панику Точнее, когда мы присваиваем результат принимающего выражения двум переменным одновременно, тип второй переменной является определенным типом bool. Если его значение равно false, это означает, что канал был закрыт, и больше нет значений элементов для приема.
Учтите, что если канал закрыт, все еще есть значения элементов, которые не были вывезены, то первым результатом принимающего выражения все равно будет значение элемента в канале, а второе значение результата должно быть истинным . Следовательно, может быть задержка при определении того, закрыт ли канал при получении второго результирующего значения выражения.
package main import "fmt" func main() { ch1 := make(chan int, 2) // 发送方。 go func() { for i := 0; i < 10; i++ { fmt.Printf("Sender: sending element %v...\n", i) ch1 <- i } fmt.Println("Sender: close the channel...") close(ch1) }() // 接收方。 for { elem, ok := <-ch1 if !ok { fmt.Println("Receiver: closed channel") break } fmt.Printf("Receiver: received an element: %v\n", elem) } fmt.Println("End.") }
Распространенные сценарии взаимоблокировки, вызванные каналом
Тупик относится к явлению блокировки из-за конкуренции за ресурсы или из-за связи друг с другом во время выполнения двух или более сопрограмм.Если нет внешней силы, они не смогут продвигаться.Метод решения тупиковой ситуации заблокирован. . В сочетании с упомянутыми выше знаниями о каналах вы можете подумать о том, почему следующая ситуация вызывает взаимоблокировку.
Сценарий 1: канал читается и записывается в сопрограмме go
func main() {
c:=make(chan int)
c<-666
<-c
}
Сценарий 2: Используйте канал до открытия процесса go
func main() {
c:=make(chan int)
c<-666
go func() {
<-c
}()
}
Сценарий 3: канал 2 вызывается в канале 1, а канал 1 вызывается в канале 2.
func main() {
c1,c2:=make(chan int),make(chan int)
go func() {
for {
select{
case <-c1:
c2<-10
}
}
}()
for {
select{
case <-c2:
c1<-10
}
}
}