Сводная информация об использовании канала Go и механизме процесса

Go

использовать канал

Распространенное использование связи между сопрограммами, отправка значений вchannel, внешнийchannelперенимать.

func t1() {
	ch := make(chan int)
	go func() {
		ch <- 1
	}()

	// <-ch // 导致下面语句阻塞
	fmt.Println("channel int", <-ch)
}
channel int 1

channelвспомогательный буфер

func t2() {
	ch := make(chan int, 3)
	go func() {
		ch <- 1
		ch <- 2
		ch <- 3
		ch <- 4 // 会阻塞写不入, 除非ch已接收
		close(ch) // 关闭后不能再写入, 并且缓冲区内为空时则返回零值
	}()
	fmt.Println("buffer channel int",
		<-ch,
		<-ch,
		<-ch,
		<-ch,
		<-ch)
}
buffer channel int 1 2 3 4 0

selectОн специально настроен для трубопровода

func t3() {
	ch1 := make(chan int)
	ch2 := make(chan struct{})

	go func() {
		ch1 <- 2
	}()

	select {
	case <-ch1:
		fmt.Println("select here is ch1")
	case <-ch2:
		fmt.Println("select here is ch2")
	}
}
select here is ch1

использоватьfor

func t4() {
	ch := make(chan int, 5)

	go func() {
		for i:=1; i<=5; i++ {
			time.Sleep(time.Millisecond * 10)
			ch <- i
		}
	}()

	for v := range ch {	// 会阻塞
		fmt.Println("for channel ", v)
		if v == 5 {
			break
		}
	}
}
for channel  1
for channel  2
for channel  3
for channel  4
for channel  5

использовать одновременноselectиfor

func t5() {
	chPrint := make(chan struct{}) 
	chStop := make(chan struct{}) 

	go func(){
		time.Sleep(time.Second * 1)
		chPrint <- struct{}{}

		time.Sleep(time.Second * 1)
		chPrint <- struct{}{}

		time.Sleep(time.Second * 1)
		chStop <- struct{}{}
	}()

	var sum int
	for {
		time.Sleep(time.Millisecond)

		select {
		case <-chPrint:
			fmt.Println("for+select now is", sum)
		case <-chStop:
			fmt.Println("for+select stop, result is", sum)
			return
		default:
			if sum == 10000 {
				fmt.Println("for+select end, result is", sum)
				return
			}
			sum += 1
		}
	}
}
for+select now is 766
for+select now is 1540
for+select stop, result is 2309

Определить, закрыта ли труба

func t6() {
	ch := make(chan struct{})

	go func() {
		close(ch)
		//ch <- struct{}{} // 只运行这句, 输出OK
	}()
	if _, ok := <-ch; ok {
		fmt.Println("if channel is ok")
		return
	}
	fmt.Println("if channel is bad")
}
if channel is bad

Используйте только отправку или только получение в качестве параметра передачи, и то же самое может быть и возвращаемым значением.

func t7() {
	ch := make(chan struct{})
	chExit := make(chan struct{})

	go func(chRecv <-chan struct{}) {
		fmt.Println("recv channel")
		<-chRecv
		chExit<- struct{}{}
	}(ch)

	go func(chSend chan<- struct{}) {
		fmt.Println("send channel")
		chSend <- struct{}{}
	}(ch)

	<-chExit
}
send channel
recv channel

Краткое изложение процесса механизма канала

makechan()инициализацияhchanструктура, выделенная, если нет буфераhchanSizeРазмер памяти и возврат; а в случае буфера вычисляется и распределяется размер типа элемента канала.hchanSize+(elem.size * size) размер памяти (буфер имеет кольцевую структуру) и, наконец, возвращаетhchan.

chansend()В направленииchannelЧтобы отправить данные, сначала заблокируйте текущую сопрограмму.Существуют следующие ситуации, которые оцениваются по порядку:

  1. Если есть ожидающий получатель, немедленно перенаправьте его получателю, снимите блокировку и выйдите.
  2. Когда буфер доступен, переместите значение в целевой буфер, чтобы дождаться его получения, снять блокировку и выйти.
  3. Выйдите, если он не блокирует (для выбора), снимите блокировку и выйдите.
  4. Заблокируйте текущую сопрограмму, поместите текущую сопрограмму в очередь ожидания отправки и снимите блокировку, а также очистите sudog после пробуждения.

chanrecv()перениматьchannelПо данным , сначала блокируем текущую сопрограмму, бывают следующие ситуации, судите по порядку:

  1. channelЗакрыть и данные не буферизуются, значение, полученное получателем, будет равно нулю, снять блокировку и выйти.
  2. Когда в очереди отправки есть отправитель, он немедленно получает данные, снимает блокировку и выходит.
  3. Если есть буферизованные данные, скопируйте данные в приемник, снимите блокировку и выйдите.
  4. Выйдите, если он не блокирует (для выбора), снимите блокировку и выйдите.
  5. Заблокируйте текущую сопрограмму, поместите текущую сопрограмму в очередь ожидания отправки и снимите блокировку, а также очистите sudog после пробуждения. иchansendПочти, есть еще одно мнение, что он закрыт и не имеет буферизованных данных.

closechanзакрытиеchannel, сначала также получите блокировку, закройте канал и освободите все очереди приема и отправки и разбудите все sudogs. Но данные в буфере не будут очищаться, ожидая получения в любое время.