goroutine
Go 语言通过 goroutine 提供了目前为止所有 (我所了解的) 语言里对于并发编程的最清晰最直接的支持,Go 语言的文档里对其特性也描述的非常全面,列举一下 goroutine 的特性:
1、goroutine 是 Go 语言运行库的功能,不是操作系统提供的功能,goroutine 不是用线程实现的。具体可参见 Go 语言源码里的pkg/runtime/proc.c
2、goroutine 就是一段代码,一个函数入口,以及在堆上为其分配的一个堆栈。所以它非常廉价,我们可以很轻松的创建上万个 goroutine,但它们并不是被操作系统所调度执行
3、除了被系统调用阻塞的线程外,Go 运行库最多会启动 $GOMAXPROCS 个线程来运行 goroutine
4、goroutine 是协作式调度的,如果 goroutine 会执行很长时间,而且不是通过等待读取或写入 channel 的数据来同步的话,就需要主动调用Gosched()来让出 CPU
5、和所有其他并发框架里的协程一样,goroutine 里所谓“无锁”的优点只在单线程下有效,如果 $GOMAXPROCS > 1 并且协程间需要通信,Go 运行库会负责加锁保护数据,这也是为什么 sieve.go 这样的例子在多 CPU 多线程时反而更慢的原因
6、Web 等服务端程序要处理的请求从本质上来讲是并行处理的问题,每个请求基本独立,互不依赖,几乎没有数据交互,这不是一个并发编程的模型,而并发编程框架只是解决了其语义表述的复杂性,并不是从根本上提高处理的效率,也许是并发连接和并发编程的英文都是 concurrent 吧,很容易产生“并发编程框架和 coroutine 可以高效处理大量并发连接”的误解。
7、Go 语言运行库封装了异步 IO,所以可以写出貌似并发数很多的服务端,可即使我们通过调整 $GOMAXPROCS 来充分利用多核 CPU 并行处理,其效率也不如我们利用 IO 事件驱动设计的、按照事务类型划分好合适比例的线程池。在响应时间上,协作式调度是硬伤。
8、goroutine 最大的价值是其实现了并发协程和实际并行执行的线程的映射以及动态扩展,随着其运行库的不断发展和完善,其性能一定会越来越好,尤其是在 CPU 核数越来越多的未来,终有一天我们会为了代码的简洁和可维护性而放弃那一点点性能的差别。