Go语言中的并发程序可以用两种手段来实现。本章讲解goroutine和channel,其支持“顺序通信进程”(communicating sequential processes)或被简称为CSP。CSP是一种现代的并发编程模型,在这种编程模型中值会在不同的运行实例(goroutine)中传递,尽管大多数情况下仍然是被限制在单一实例中。
在Go语言中,每一个并发的执行单元叫作一个goroutine。设想这里的一个程序有两个函数,一个函数做计算,另一个输出结果,假设两个函数没有相互之间的调用关系。
a.一个线性的程序会先调用其中的一个函数,然后再调用另一个。
b.如果程序中包含多个goroutine,对两个函数的调用则可能发生在同一时刻。
如果你使用过操作系统或者其它语言提供的线程,那么你可以简单地把goroutine类比作一个线程,这样你就可以写出一些正确的程序了。
当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们叫它main goroutine。
新的goroutine会用go语句来创建。在语法上,是一个普通的函数或方法调用前加上关键字go。
go语句会使其语句中的函数在一个新创建的goroutine中运行。而go语句本身会迅速地完成(意思就是创建goroutine的语句会立即执行,但是这个函数的执行时刻并不确定)。
示例:
f() // call f(); wait for it to return
go f() // create a new goroutine that calls f(); don't wait
下面的例子,main goroutine将计算菲波那契数列的第45个元素值。
由于计算函数使用低效的递归,所以会运行相当长时间,在此期间我们想让用户看到一个可见的标识来表明程序依然在正常运行,所以来做一个标记的打印:
package main import "fmt" import "time" func main() { go spinner(100 * time.Millisecond) const n = 45 fibN := fib(n) // slow fmt.Printf(" Fibonacci(%d) = %d ", n, fibN) } func spinner(delay time.Duration) { for { for _, r := range `-|/` { fmt.Printf(" %c", r) time.Sleep(delay) } } } func fib(x int) int { if x < 2 { return x } return fib(x-1) + fib(x-2) }
主函数返回时,所有的goroutine都会被直接打断,程序退出。除了从主函数退出或者直接终止程序之外,没有其它的编程方法能够让一个goroutine来打断另一个的执行,但是之后可以看到一种方式来实现这个目的,通过goroutine之间的通信来让一个goroutine请求其它的goroutine,并让被请求的goroutine自行结束执行。
留意一下这里的两个独立的单元是如何进行组合的,spinning和菲波那契的计算。分别在独立的函数中,但两个函数会同时执行。