2015-04-14 wcdj
在GoLang之Concurrency再讨论一文讨论过并发的几个问题,分别如下:
1,goroutine是否并发的问题
2,goroutine非并发安全性问题
3,并发情况下的原子操作问题
本文在上述讨论的基础上,进一步对goroutine的调度机制,以及并发的实现原理进行讨论。
0 goroutine 轻量的调度单位
程序员使用goroutine可以快速地开发一个并发(可能非并行)执行的程序。goroutine是协程(coroutine,轻量级进程,Light Weight Process)的一种实现,Linux POSIX C也提供了coroutine系统调用。协程(轻量级进程),就是在用户空间实现调度,比线程更轻量的执行单元。协程的调度不是抢占式的,这就意味着同一线程内的协程只有主动放弃cpu,调度器才会将另外一个等待的协程调度进来然后执行。goroutine在Go语言的运行时环境(runtime)中实现,在用户空间内被调度于一个或多个线程之间(而非限定在一个线程中)。当一个goroutine调用了某个可能阻塞的标准库函数之后,这个goroutine则会被运行时环境暂停,另一个准备好的goroutine则被调度进来然后开始执行。
原则上,Go语言运行时环境中的调度器与操作系统的调度器并没有太大区别,都是在某个事件发生时,将最小调度单位暂停或启动,但goroutine的调度在用户空间内执行,因此调度速度更快,并且可以支持大量并发的goroutine同时运行。
Go语言是一个完全编译型的语言(静态语言),它的代码被直接编译成本地机器码执行,因此不存在中间的解释器或者虚拟机。要实现goroutine运行时的功能,Go语言是利用运行时环境来完成的。Go的程序在链接时,都会被加入一套运行时环境。这套环境类似于一个静态库,包含来Go语言中规定的一些运行时特性,例如,垃圾回收,goroutine调度等。在Go语言标准库中,在每个可能产生阻塞的函数中,都会最终通知运行时环境中的调度器,让调度器将当前的goroutine暂停,这样的设计可以极大地简化并发(可能非并行)程序的开发。
1 goroutine与丢失的select/poll/epoll等非阻塞调用
在Go语言中,是否提供了类似C语言中select/poll/epoll函数?如何让一个网络连接使用非阻塞I/O?怎么对网络连接进行异步读写?
这几个问题回答是:Go的网络程序不使用异步操作,一切操作都是同步的,每个goroutine处理一个连接。
在Go代码层面,开发者看到的是使用goroutine来进行阻塞式读写,而在Go的内部实现中,则是利用异步操作,通过对goroutine的调度来完成对事件的处理。作为最小的调度单位,goroutine之间是并发(可能非并行)执行的。