Go 并发
传统的并发是通过进程、线程、I/O多路复用来实现,现在越来越多的语言支持基于协程(coroutine)的并发模型。如:Actor模型的Akka/Erlang、CSP模型的Goroutine/Go。
操作系统层的两个比较知名的“协程”接口族分别是:Microsoft Windows的fiber和POSIX的ucontext(linux实现该接口)。
Go语言中的并发程序可以用两种手段来实现:goroutine/channel、多线程共享内存(传统并发模型)。
一、Goroutines和Channels
在Go语言中,当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们称之为main goroutine。新的goroutine会用go语句来创建。
func main() { go spinner(100 * time.Millisecond) const n = 45 fibN := fib(n) // slow fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN) } func spinner(delay time.Duration) { for { for _, r := range `-\|/` { fmt.Printf("\r%c", r) time.Sleep(delay) } } } func fib(x int) int { if x < 2 { return x } return fib(x-1) + fib(x-2) }
如果说goroutine是Go语言程序的并发体的话,那么channels则是他们之间的通信机制。channel可以让一个goroutine通过它给另一个goroutine发送值信息。
每个channel都有一个特殊的类型,也就是channels可发送数据的类型。一个可以发送int类型数据的channel一般写作chan int。
使用内置的make函数,我们可以创建channel:
ch := make(chan int) // ch has type 'chan int'
一个channel有发送和接收两个主要操作,都使用<-运算符。如:
ch <- x // a send statement x = <-ch // a receive expression in an assignment statement <-ch // a receive statement; result is discarded
channel还支持close操作,用于关闭channel,随后对基于该channel的任何发送操作都将导致panic异常。
对一个已经被close过的channel进行接收操作依然可以接收到之前已经成功发送的数据,如果channel中已经没有数据的话将产生一个零值的数据。
channel分为有缓存和无缓存两种,通过make创建时指定第二个整型参数,大于零时为带缓存的channel。
close(ch) // close channel ch = make(chan int) // unbuffered channel ch = make(chan int, 0) // unbuffered channel ch = make(chan int, 3) // buffered channel with capacity 3
待续...