go语言教程:
go以高并发语言著称,而在go语言中,关键字go就是开启多线程的关键,下面做一个示例如下
//goTest.go
package main
import (
"fmt"
"time"
)
func count(s string) {
for i := 0; i < 3; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s, i)
}
}
func main() {
go count("A:")
count("B:")
}
其中,count
是一个非常简单的函数,每间隔100毫秒就打印一个数字。在主函数中,先用go开启一个线程运行count(“A:”),然后用主线程运行count(“B”),运行结果如下
>go run goTest.go
B: 0
A: 0
A: 1
B: 1
B: 2
这里反映出两个现象,首先A和B的确是交替执行的,但A最后输出的数值是1,换言之,用go指令开启的线程,会在主线程结束之后自动销毁。
并发的最大问题是线程通信,go语言为此打造了一种特殊的数据类型chan
,即通道。而通道的通信方式,则经由一个特殊的操作符<-
,形如c <- d
的表达式,意味着将d的值传递给通道c;反过来说,d <- c
则是将通道c传递过来的内容复制给d。
下面对count函数稍加改动,为其新增一个通道变量c,并且在循环时并不直接打印变量,而是将其内容输出给c。
相应地,主函数也进行更改,并打印通道c中接收到的内容。
// chanTest.go
package main
import (
"fmt"
"time"
)
func count(s string, c chan string) {
for i := 0; i < 3; i++ {
time.Sleep(100 * time.Millisecond)
c <- fmt.Sprintf("%s%d", s, i)
}
}
func main() {
c := make(chan string)
go count("A:", c);
go count("B:", c);
for i:=0; i<3; i++{
x, y := <-c, <-c // 从通道 c 中接收
fmt.Println(x, y)
}
}
运行结果如下
>go run chanTest.go
B:0 A:0
B:1 A:1
A:2 B:2
select...case
是一种为了并发通信而设计的分支结构,其语法格式与switch如出一辙,但case后面必须是一个通道的通信操作。
下面用这个结构,来做一个斐波那契数列的生成函数,代码内容如下
// select.go
package main
import "fmt"
func fib(c,quit chan int){
x,y:=0,1
for{
select{
case c <- x: x,y = y, x+y
case <-quit: return
}
}
}
func fibTo(N int, c,quit chan int){
for i := 0; i < N; i++{
fmt.Println(<-c)
}
quit <- 0
}
func main(){
c := make(chan int)
quit := make(chan int)
go fibTo(15, c, quit)
fib(c,quit)
}
其中,fib函数有两个输入参数,分别是通道c和quit,当c发送数据数据时,执行斐波那契过程;当quit受到数据时,跳出死循环。
fibTo函数除了这两个通道之外,还有一个整数N,表示斐波那契数列的终止下标。其内部只有一个循环,无需多言,当循环执行结束后,向quit通道发送一个0,这时fib函数中的select…case就进入到了quit接收,从而跳出。
其运行结果为
>go run select.go
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377