谭浩的博客

Simple is beauty.

Goroutine 和 Thread

在学习6.824课程的时候接触到了 Go 语言,其由 google 创造的且正在云计算和区块链领域大展伸手,比如docker、etcd、超级账本fabric等。在我看来,这是个简单粗暴美的编程语言,相比于 Java 更显轻盈,Java 相对来说更加中庸。而最吸引我的就是其并发机制,让菜鸟的我能快速上手的写并发项目。而这篇小笔记就是对Goroutine 和 Thread 的对比。快速了解 Go 并发可以浏览这篇文章

Goroutine 的优点

  • 在同样的系统上,相比线程可以运行更多的 goroutine
  • Goroutine 拥有按需增长的栈空间
  • Goroutine 启动的速度更快
  • Goroutine 通过内置的原语(channel)安全地通信
  • 当共享数据结构时,goroutine 可以避免求助于互斥锁
  • Goroutine 被多路复用到系统线程上
  • 不需要借助于事件就可以编写大型并发服务器

运行更多的Goroutines

Java 线程直接映射到操作系统的线程上,和 goroutine 相比是更重量级的。Java 线程具有固定的栈空间,因此限制了线程数量,而 goroutine 具有按需增长的栈空间。其次,Java 线程映射到操作系统的线程,线程调度由操作系统执行,而每次线程调度都需要从用户态切换到内核态,需要进行相应的上下文切换,在切换时需要保存PC值、线程栈空间等,导致调度代价高于 goroutine。Goroutine 被多路复用到系统线程上, goroutine 的调度由 Go Runtime 执行,运行在用户态。当 goroutine 阻塞时(例如调用阻塞系统调用),则由 Go Runtime 自动将同一操作系统线程上的其他 goroutine 移动到其他可运行的线程上,如此其他 goroutine 不会发生阻塞,而且 goroutine 阻塞 IO 则由 epoll 或者其他相似的机制高效处理。

避免锁

多线程最大的缺点就是其复杂性,难以实现高并发情况下的正确性。死锁问题、数据竞争让多线程编程变得异常困难。Go 提供了内置的原语用来避免锁的使用。其有句著名的口头禅:don’t communicate by sharing memory, share memory by communicating。简单来说,在 Go 中,如果 goroutine 之间需要共享数据,可以通过 channel 实现,而不需要使用锁。Go 为用户处理同步问题,从而也不容易发生死锁。