Golang-Channel & Goroutine-进阶篇

基础篇简单了介绍Channel&Goroutine的基本使用方法
接下来就是实际应用的问题了

Select

实际例子上可能会有1-N个不等的chaneel
这时候就需要使用专门给channel使用的switch case,叫做Select
Select有几个地方要注意

  • 只能给channel用
  • 假设同时有两个channel达成条件,他会随机选择一个case进行

语法请看综合范例2有演示

Goroutine

使用Goroutine最重要的
不要对routing的输出顺序以及时间做出假设

举个简单的例子,只要印出123

func main() {
	go func() {
		log.Println("123")
	}()
	//没有暂停1秒钟让另一个routing准备好,有可能程序会直接结束而不会输出任何东西
	time.Sleep(1 * time.Second)
}

可以试试看把time.Sleep(1 * time.Second)给Remark,基本上是不会输出任何东西,至少我是没有成功过
因为Goroutine有极大的可能在main function执行完时而还没有开始执行
所以Goroutine的closure就会什麽都没有执行就结束了

Channel搭配Goroutine

这是最重要也是最难的部分
在使用Channel的时候最常要注意的事情就是deaklock
因为会deadlock的案例实在不胜枚举,这边会提供给大家正确的做法,会有deadlock的情境会尽量提到

UnBuffered Channel

基础篇有提到UnBuffered Channel有着同步的特性
这种channel的两端会等待着写入与读取,而且因为没有Buffer,所以Channel的两端会同步资料

func main() {
	ch := make(chan bool)
	go func() {
		ch <- true
	}()

	// 虽然没有time.Sleep等待goroutine执行
	// 但最後有一个<-ch一直读取channel
	// 所以可以看到Print true
	log.Println(<-ch)
}

Buffered Channel

可以使用for range方法读取里面的资料

func main() {
	ch := make(chan int, 10)
	go func() {
		for i := 0; i < 10; i++ {
			ch <- i
		}
		close(ch)
	}()

	for value := range ch {
		log.Println(value)
	}
}

综合范例1 - 1个Routing + 1个channel + for loop

从channel利用for loop读取资料,可以搭配基础篇服用
make channel的那一行可以加上cap玩看看,马上就能知道UnBuffered & Buffered的差异
close(ch)那段最重要,可以试试看remark的结果,保证deadlock

func main() {
	ch := make(chan int) // 可以加上cap,一样可以正常跑程序,而且可以看出UnBuffered & Buffered的差异
	go func() {
		for i := 0; i < 10; i++ {
			log.Println("Send value", i)
			ch <- i
		}
		close(ch) // close channel很重要,没有闭关的话,另一端一直在读取,而没有资料写入,就会产生deadlock
	}()

	for {
		val, ok := <-ch
		if ok {
			log.Println("Receive value", val)
		} else {
			break
		}
	}
}

综合范例2 - N个Routing + N个channel + for loop + select

执行这段程序码有个重点
不要预期routing的执行时间,先後顺序,一切的假设都不要有
for loop的部分是要一直读取channel value
而当x==20时就会触发事件s<-str
select接收到s channel的value时就会执行该case,跳出带有LOOP tag的for loop

func main() {
	str := "stop"
	c := make(chan int)
	s := make(chan string)

	for i := 0; i < 50; i++ {
		go func(x int) {
			if x == 20 {
				log.Println("goroutine will stop")
				s <- str
			} else {
				log.Println("Send value", x)
				c <- x
			}
		}(i)
	}

LOOP:
	for {
		select {
		case v, ok := <-c:
			if ok {
				log.Println("Select value", v)
			}
		case str := <-s:
			log.Println(str)
			break LOOP
		default:
			log.Println("wait")
		}
	}
}

小结

在使用Goroutine&channel的时候要注意的地方大致可以归类在以下几点

  • 同步使用
  • 非同步使用
  • channel是否已经闭关
  • channel是否已满
  • 不要对routing做出执行顺序、执行时间的假设

然後要自己写一遍,多deadlock几次到差点想把笔电一分为二的时候你就会了(误

其他还有Package sync & Package runtime,就等下一篇吧


<<:  软件保障成熟度模型(Software Assurance Maturity Model :SAMM)

>>:  React和DOM的那些事-节点删除算法

【Day 9】Google Apps Script - 部署网页应用程序与触发doGet(e)测试

「查询Gamil资讯」API 实作完成,那就可以部署上线测试啦。 今日要点: 》部署 API 》呼...

铁人赛Day29-第九章:动画5.1-天气与湾熊1

今天来进入下一个动画,这次的主题是描述湾熊(自创角色)的日常,那麽,我们开始吧! 1. 2. 3. ...

[24] 用 python 刷 Leetcode: 66 plus-one

原始题目 You are given a large integer represented as ...

未来狂想:金融领域

人的科技文明发展始终来自於人性 在科技的发展与技术的发展之下,在很多的领域都有许多的应用,甚至因为科...

30天轻松学会unity自制游戏-添加音效

现在再加上一点音效做装饰,先给一个背景音乐,直接在Hierarchy按右键开启Audio->A...