每期一个小窍门: 通过 两个小例子 玩转go channel

关于go中的通道可以这样理解:

  • 通道类似一个没有持久化功能的, 完全基于内存的, 消息队列
  • 若创建通道时未设置通道容量,则协程在向通道中存放数据后会一直等待,直到其它协程取走数据后才会向下执行;
  • 若创建通道时设置通道容量,则协程在向通道中存放数据后会继续向下执行, 不会阻塞
  • 在向通道中存放数据时,若通道已满,则当前协程会一直等待,直到通道空闲;
  • 在从通道中读取数据时,若通道中不存在数据,则当前协程会一直等待,直到其它协程向通道中存放数据。

下面举个例子

func TestDoSomething05(t *testing.T) {
    
    

	res := asyncService()
	task02()
	time.Sleep(time.Second * 5)
	fmt.Println("接收到", <-res)
	time.Sleep(time.Second * 1)
}
func task02() {
    
    
	fmt.Println("task 02 done")
}
func task01() string {
    
    
	return "task 01 done"
}
func asyncService() chan string {
    
    
	retCh := make(chan string)
	go func() {
    
    
		ret := task01()
		fmt.Println("start....")
		retCh <- ret
		fmt.Println("end....")
	}()
	return retCh
}

# 执行结果会先打印出

task 02 done
start....

注意: 此时5秒钟过去

接着打印

接收到 task 01 done
end....

这时会执行完毕
明显会看到 fmt.Println("end....") 这行代码被阻塞了5秒

阻塞的原因就是:

  • 上面我说过的, 重复一遍, 若创建通道时未设置通道容量,则协程在向通道中存放数据后会一直等待,直到其它协程取走数据后才会向下执行
  • 示例1没有设置通道容量, 同时通道接收端有5秒的sleep, 造成了这个情况

下面我进行如下修改

  • 我设置通道容量, 使通道变为缓存通道
    也就是这样:
retCh := make(chan string, 2)

代码就变成了如下结构

func TestDoSomething05(t *testing.T) {
    
    

	res := asyncService()
	task02()
	time.Sleep(time.Second * 5)
	fmt.Println("接收到", <-res)
	time.Sleep(time.Second * 1)
}
func task02() {
    
    
	fmt.Println("task 02 done")
}
func task01() string {
    
    
	return "task 01 done"
}
func asyncService() chan string {
    
    
	retCh := make(chan string, 2)
	go func() {
    
    
		ret := task01()
		fmt.Println("start....")
		retCh <- ret
		fmt.Println("end....")
	}()
	return retCh
}

# 执行结果会先打印出

task 02 done
start....
end....

注意: 此时5秒钟过去

接着打印

接收到 task 01 done

明显会看到 fmt.Println("end....") 这行代码没有被阻塞
仅仅只有主线程的fmt.Println("接收到", <-res)
也就是只有通道的另一端接收者
被sleep 等待了5秒

还有一句想说的:
可能也是最重要的
我看好多人都被日志前后顺序迷惑了,
不要被fmt 或者 t.log 迷惑 chan 接到消息 到 打印日志 这还有一段路要走
哪边走的快这个不一定的 两回事 要谨慎使用sleep时间来写demo 位置/时间差一定要合适

猜你喜欢

转载自blog.csdn.net/qq_33709508/article/details/132288195
今日推荐