教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 go channel 使用及机制流程汇总

go channel 使用及机制流程汇总

发布时间:2021-05-13   编辑:jiaochengji.com
教程集为您提供go channel 使用及机制流程汇总等资源,欢迎您收藏本站,我们将为您提供最新的go channel 使用及机制流程汇总资源

使用channel

协程间通讯的普通使用, 发送值给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
复制代码

同时使用selectfor

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
复制代码

channel机制流程汇总

makechan()初始化hchan结构体, 如果没有缓冲区即分配hchanSize大小的内存并返回;而有缓冲区的情况下, 则计算管道元素类型大小并分配hchanSize (elem.size * size)大小的内存(缓冲区是一个环形的结构设计), 最后返回hchan.

chansend()channel发送数据, 首先锁住当前协程, 有如下几种情况, 按顺序判断:

  1. 有正在等待的接收者, 就立即转发给该接收者, 释放锁并退出.
  2. 有可用的缓冲区就将该值移到目标缓冲区等待被接收, 释放锁并退出.
  3. 是非阻塞(用于select)就退出, 释放锁并退出.
  4. 阻塞当前协程, 并将当前协程放到发送等待队列中并释放锁, 唤醒后清理sudog.

chanrecv()接收channel的数据, 首先锁住当前协程, 有如下几种情况, 按顺序判断:

  1. channel关闭并没有缓冲数据, 接收者接收的值将会是零值, 释放锁并退出.
  2. 发送队列有发送者, 就立即接收该数据, 释放锁并退出.
  3. 有缓冲数据就将该数据复制给接收者, 释放锁并退出.
  4. 是非阻塞(用于select)就退出, 释放锁并退出.
  5. 阻塞当前协程, 并将当前协程放到发送等待队列中并释放锁, 唤醒后清理sudog. 跟chansend差不多, 多了个已关闭并没有缓冲数据的判断.

closechan关闭channel, 首先也要获取锁, 关闭管道并释放所有接收和发送的队列并清醒所有sudog. 但缓冲区的数据不会清理, 随时等待被接收.


到此这篇关于“go channel 使用及机制流程汇总”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
go channel 使用及机制流程汇总
golang学习笔记(二)—— 深入golang中的协程
Go并发模式:管道和取消
go语言并发编程
Go并发编程——channel
Golang学习小结、从入门到精通资料汇总
浅谈 Go 语言 select 的实现原理
图解 Go 并发编程
【文末有惊喜!】一文读懂golang channel
图解Go的channel底层原理

[关闭]
~ ~