第一次用 Golang 出面试题小记
最近一直在学习 Golang
,正好今天遇到一个七牛的候选人,就顺带出了一个多线程的题目让他用 Golang
写,在他写的过程中我担心自己没法给他 review code (我也是个新手),就在自己的电脑上尝试了一把,rush 的比较快,但是还是遇到了 docklock
的问题,在这里记录一下
考题
用两个goroutine
分别打印奇数和偶数的方式,输出1~10
实现思路
这个就是考核一下线程切换的,在java
基本就是靠object.wait
和 object.notifyAll
来实现,不够用Golang
的channel 感受上会不太一样
这个题目的关键点有3个
- 确保奇数的
goroutine
是一定在偶数之前运行的 - 奇数和偶数的交替输出
- 程序正常退出
想法上就是弄4个channel
startChan
负责保证奇数一定先开始notifyEven
和notifyOdd
用来做线程切换done
用来做任务退出
1 | startChan := make(chan int) |
buffer 的设计主要是done
, 因为有2个goroutine
, 防止在写入的时候block,给了2的buffer
Goroutine 代码
####奇数代码
1 | go func() { |
####偶数代码
1 | go func() { |
####主函数代码
1 | startChan <- 1 |
解释一下
- 奇数线程pending 在
<-startChan
上,等主线程startChan <- 1
来唤醒 - 奇数偶数之间互现唤醒和沉睡,做完全部工作后,调用
done <- 1
- 主线程 block 在
<-done
上,等所有工作线程做完了,主线程组再推出
一切看似很快就写完了,但是一运行,还是遇到了deadlock
问题
问题排查和改进
deadlock bug fix
怎么会deadlock
呢,想了一下,哦,原来奇数再打印了9以后就推出了,但是这个时候偶数还block在<-notifyEven
上,所以就死锁了饿,Fix 非常简单,退出的时候通过notifyEven <- 1
把偶数唤醒就好了
sync.WaitGroup
done
这个事情看起来就是以前 java 里面的join
在做的事情,这种common的事情还要自己写,太low了,查了一下原来golang是有一个sync
库做类似的事情,用waitGroup
替换done
就好了,最后贴一下现在的代码
1 | startChan := make(chan int) |
此刻我想说一句,go写并发真有趣