21天从Java转向Go之第九天——水滴石穿(gorutine和通道chan)
goroutine
CSP模型
- (Coummunicating Sequential Process)通信顺序进程,CSP是一个并发模式,在不同的执行体(goroutine)之间传递值,但是变量本身局限于单一的执行体。
- 当一个程序启动时,只有一个goroutine来调用main函数。称它为主goroutine。
- 语法:在普通的函数或方法调用前加上go关键字前缀。go语句使函数在一个新创建的goroutine中调用。go语句本身的执行立即完成:
f() //调用f(),需要等待它返回
go f() //新建一个调用f()的goroutine,调用完立即返回
- 斐波那契数列demo
package main
import (
"fmt"
"time"
)
func main() {
go func() {
for {
for _, v := range `_/|\` {
fmt.Printf("\r%c", v)
time.Sleep(time.Second)
}
}
}()
fmt.Println(fib(50))
}
/**
i index 0,1,2,3,...
return v
*/
func fib(i int) int {
if i < 2 {
return i
}
return fib(i-1) + fib(i-2)
}
通道chan
- 通道是可以让一个goroutine发送特定值到另一个goroutine的通信机制。每个通道是一个具体类型的导管,叫做通道的元素类型。一个有int类型的通道写为chan int。make函数可以指定容量,创建一个带缓冲的通道。
var ch = make(chan int)
- 像map一样,通道是一个使用make函数创建的数据结构的引用。当复制或者作为参数传递到一个函数时,复制的是引用。这样调用者和被调用者都是引用同一份数据结构。通道的零值是nil。
- 同种类型的通道==判断时,返回true
- 通道两个操作:发送和接收
ch <- x //发送语句
<-ch //接收语句 丢弃结果
y := <-ch //赋值语句中的接收表达式
close(ch) //关闭通道
- 无缓冲通道(同步通道)上的发送操作将会阻塞,直到另一个goroutine在对应的通道上执行接收操作,这时值传送完成,两个goroutine都可以继续执行。相反,如果接收方先执行接收操作,将阻塞直到另一个goroutine在同一个通道上发送一个值。
- 单向通道 在函数参数上声明指定类型,传入实参时会隐式转换为参数要求的单向通道类型,但是反过来不行,不能由单向通道类型引用到底层同一个数据结构的chan的类型
chan <- int //只能发送类型的通道
<-chan int //只能接收int类型的通道
-
缓冲通道,在make函数指定容量。缓冲通道上的发送操作在队列的尾部插入一个元素,接收操作从队列的头部移除一个元素。如果通道满了,会阻塞发送操作,通道空了,会阻塞接收操作。(类似于阻塞队列)
-
cap(ch) 获取通道的容量
-
len(ch) 获取通道元素的个数
并行循环
func loop(slices []int) {
result := make(chan int)
for _, v := range slices {
//v的值被所有的匿名函数值共享并且被后续的迭代更新。
//新的goroutine执行字面量函数,for循环可能已经更新了v,所以当这些goroutine读取f的值时,
//它们所看到的都是slice的最后一个元素。通过添加显示参数,可以确保当go语句执行的时候,使用f的当前值。
go func(v int) { //捕获迭代变量 每次循环产生的函数,其记录的是循环变量的地址,所有循环产生的函数公用这一个值。
r := v * v
result <- r
}(v)
}
for _,v := range slices {
fmt.Println("v的平方:",v,<-result)
}
}