最近有时间也有必要接触了Go,也有很多原因
- 大三的时候就想学学Go,不过由于Python耽搁了
- Python的并发让我头很疼,一点也不优雅,而Go的天然并发早声名在外
- 业务需求
- 多学一门语言,提升自己的竞争力
Go的特点
学习Go几乎没有阻碍,跟着A Tour of Go一步步学下来就行。光学怎么写Go就太枯燥了。我顺便了解了一下Go,很多内容在wiki中已经说得很清楚。总结一下,
- Go是静态类型,与C/C++类似,不同于Python,因此性能肯定比基于解释器的Python高。Go有垃圾回收机制、天然并发,写起来就很舒服,代价是性能肯定没有C那么高,看到一句话很有意思:Informally, Go is 50% of C’s performance (which is still really good) for 0% of the headaches.
- Go这个名字取得很好,Go由Google成员开发,Go是围棋,也是gopher(鼹鼠),为什么gopher是吉祥物?只能解释为男人的浪漫了。
- Go是面向多核的语言,也就是面向未来。Go在Google中也有不少应用,我只找到古老的资料,Rob Pike的演讲、Quora的问答、关于dl.google.com的故事也很有意思。
- Go没有类,但有结构体,我喜欢这点。
- Go有指针,太棒了。
- Go的interface设计太巧妙了。
- Go的goroutine就不用说了,强、优雅。但我目前不知道CSP的细节。
- Go的包管理很出色。
体验了2天Go,我无法找到Go的缺点,即使是缺点在我看来也是优点(没有面向对象)。当然Go是否是面向对象的也很难说,可以看看这篇讨论,反正我是认为这是Go的优点。我已经把Go列为第二优先语言了,学了Go可能就不用再学C/C++了。
学习和使用Go的同时能开拓新的思考方式、思维模式,就像学外语一样,思维方式会得到提升。而Go带来的思维改变就是思考要并发。当然并发不是并行,这个可以看Rob Pike的演讲,还有关于并发模式的演讲。形象的说,并发是一个人可以一边吃饭一边喝水,可以随时切换,而并行一般是做同样的事,比如左手和右手同时转笔。我觉得并发有并行的意思,可能之后还要深入理解一下,不过理解了也没啥用。
Go的并发
Go其他部分比较简单,随便学学就行,我觉得并发是Go的精髓。Go的并发很好用很优雅,可以看看性能如何。我将Go与我常用,让我头疼的Python作了对比。首先是裸的并发,就算个加法,也不join,只看起并发的开销。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import threading import time def add(left, right): return left + right def plain_thread(n): for i in range(n): t = threading.Thread(target=add, args=(0, 0)) t.start() return 0 if __name__ == "__main__": n = 8000 start_time = time.time() ret = plain_thread(n) total_time = (time.time() - start_time) * 1000 print(ret, total_time, total_time / n) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package main import ( "fmt" "time" ) func add(left, right int) int { return left + right } func plainThread(n int) int { for i := 0; i < n; i++ { go add(0, 0) } return 0 } func main() { const cs = 8000 startTime := time.Now().UnixNano() finalV := plainThread(cs) totalTime := float64(time.Now().UnixNano() - startTime) / 1e6 fmt.Println(finalV, totalTime, totalTime / cs) } |
虽然这个比较不科学,但也可以跑一下试试。
接下来跑个需要同步的,用到了并发常用的queue或channel。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import queue import threading import time def daisy_add(left, right): item = right.get() left.put(item + 1) def daisy_chain(n): leftmost = queue.Queue() right = leftmost left = leftmost for i in range(n): right = queue.Queue() t = threading.Thread(target=daisy_add, args=(left, right)) t.start() left = right right.put(1) r = leftmost.get() return r if __name__ == "__main__": n = 8000 start_time = time.time() ret = daisy_chain(n) total_time = (time.time() - start_time) * 1000 print(ret, total_time, total_time / n) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package main import ( "fmt" "time" ) func daisyAdd(left, right chan int) { left <- 1 + <- right } func daisyChain(n int) int { leftmost := make(chan int) right := leftmost left := leftmost for i := 0; i < n; i++ { right = make(chan int) go daisyAdd(left, right) left = right } //go func(c chan int) { c <- 1}(right) right <- 1 finalV := <- leftmost return finalV } func main() { const cs = 8000 startTime := time.Now().UnixNano() finalV := daisyChain(cs) totalTime := float64(time.Now().UnixNano() - startTime) / 1e6 fmt.Println(finalV, totalTime, totalTime / cs) } |
上面两段代码极其相似,这也让我对channel的理解深了一点,功能上就相当于Python中的queue。粗略一测,Go和Python时间性能相差30倍。另外,这段代码中,Python在我的电脑上起不了10000个线程,Go可以起1000000个,我还没测Go的上界。
为什么goroutine这么好用?我搜索了一下,发现很多好的资源,
- goroutine和thread的对比,佩服写得好
- goroutine不是轻量线程,Rob Pike也说了,理解这点很重要。看了这个说明,完全就理解了
总结
以上是我接触了2天Go写的胡言乱语,希望以后看到不会脸红。总之,Go是不错的语言,值得学习。特别是现在很多情景对并发的需求越来越大、C/C++等语言的设计考虑、硬件的分布式等,Go就是顺应时代潮流的产物,Google还是爸爸。
感叹一句,什么时候我才能那么屌?
参考
- Analysis of the Go runtime scheduler,以后看
- Five things that make Go fast,很有深度,看了之后脑子炸了,还有一系列
近期评论