go语言使用chan的小技巧

技巧1:关闭某个chan时,所有阻塞读取该chan的协程都会收到通知

举例:如果协程A希望协程B在处理完某个事情后自己能收到通知,可以向协程B的函数参数中传入一个chan,然后协程A阻塞读取该chan。协程B在需要通知其的时候关闭chan即可,这样协程A就会返回。

常用于:某个协程内新建了一个子协程,但是希望子协程在某个时候,例如初始化完成时通知自己

注意事项:是直接关闭chan就可以了,不需要向这个协程内压入数据,因为压入数据的话最终还得关闭chan

代码实验:

1. 主协程创建一个子协程,在子协程的函数参数中传入一个无缓冲的chan

2. 主协程阻塞读取这个chan

3. 子协程在2秒后关闭这个chan,可以发现主协程从阻塞中返回

实验结果如下图(可以发现子协程一旦关闭chan,主协程立马就从阻塞中返回了)

完整代码:

package main
import (
	"fmt"
	"time"
)
func func1(c chan bool) {
	fmt.Println("func1 init start, time=", time.Now().UnixMilli())
	// 用sleep模拟初始化工作
	time.Sleep(time.Second)
	fmt.Println("func1 init end,   time=", time.Now().UnixMilli())
	close(c)

	time.Sleep(time.Second)
	fmt.Println("func1 end, 	  time=", time.Now().UnixMilli())
}

func main() {
	fmt.Println("main start,       time=", time.Now().UnixMilli())
	c := make(chan bool, 1)
	go func1(c)
	// 开始阻塞等待
	<-c
	fmt.Println("main continue,    time=", time.Now().UnixMilli())
	time.Sleep(time.Second * 2)
}

技巧2:向无缓冲的chan中写数据会阻塞,直到另一方从该chan取走数据,写入方才会返回

举例:如果协程A希望协程B能立马处理自己的请求,应该使用无缓冲chan,让协程B在select中处理

常用于:一个协程希望另一个协程立即处理自己的请求

注意事项:

1. 因为会阻塞写入的协程,建议在单独协程中向无缓冲通道中写

2. 正常代码向无缓冲chan中写数据会阻塞,但是在select代码中向无缓冲chan中写数据不会阻塞写入的协程,写入的协程会继续执行其他代码,完全不影响,也就是下面要说的技巧3

代码实验:

1. 主协程创建一个子协程,在子协程的函数参数中传入一个无缓冲的chan

2. 主协程向这个chan写入数据,可以看到主协程阻塞

3. 子协程在休眠2秒后才从chan中读出来数据,可以发现主协程从阻塞中返回了

实验结果如下图(可以发现主协程在阻塞,子协程休眠结束从chan中读取数据后,主协程立马就从阻塞中返回了)

 完整代码:

package main
import (
	"fmt"
	"time"
)
// 独立协程
func goroutine1(c chan int) {
	// 休眠2秒
	time.Sleep(time.Second * 2)
	fmt.Println("子协程开始从c读取, 	time=", time.Now().Unix())
	// 从c中读取
	<-c
	fmt.Println("子协程从c读取结束, 	time=", time.Now().Unix())
}
func main() {
	c := make(chan int)
	go goroutine1(c)
	fmt.Println("主协程开始向c写入,       time=", time.Now().Unix())
	c <- 1
	fmt.Println("主协程向c写入结束,       time=", time.Now().Unix())
	time.Sleep(time.Second * 3)
}

技巧3:在select中向无缓冲的chan中写数据不会阻塞写入协程

举例:如果协程A希望协程B能立马处理自己的请求,但是又不希望自己阻塞等待,应该使用无缓冲chan,让协程B在select中处理

常用于:一个协程希望另一个协程立即处理自己的请求,但自己不想阻塞等

注意事项:

写入一方要在select中写入

代码实验:

1. 主协程创建一个子协程,在子协程的函数参数中传入一个无缓冲的chan

2. 主协程在select中向这个chan写入数据,可以看到主协程没阻塞,该干嘛干嘛,定时器还在触发

3. 子协程每休眠3秒后才从chan中读出来数据,可以发现每次子协程读取完成一次,主协程的select都会返回一次

实验结果如下图(可以发现主协程完全没阻塞,定时器仍然在触发。而且每次子协程从chan中读取数据后,主协程立马能从select中返回了)

 完整代码:

package main
import (
	"fmt"
	"time"
)
// 独立协程
func goroutine1(c chan int) {
	for {
		time.Sleep(time.Millisecond * 3500)

		fmt.Println("子协程开始从无缓冲通道接收数据, time=", time.Now().UnixMilli())
		<-c // 从无缓冲通道中读取
		fmt.Println("子协程从无缓冲通道接收数据完成, time=", time.Now().UnixMilli())
	}
}
func main() {
	c := make(chan int)
	go goroutine1(c)
	syncTicker := time.NewTicker(time.Second)
	for {
		select {
		case <-syncTicker.C:
			{
				fmt.Println("主协程的定时器触发, time=", time.Now().UnixMilli())
			}
		case c <- 1:
			{
				fmt.Println("主协程向无缓冲通道写入数据完成了, time=", time.Now().UnixMilli())
			}
		}
		// 其他代码
		// todo
		fmt.Println("主线程跳出了select, time=", time.Now().UnixMilli())
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值