goroutine
通信顺序进程CSP,是一个并发的模式,在不同的执行体(goroutine)之间传递值,但是变量本身局限于单一的执行体。
每一个并发执行的活动称为goroutine;
当一个程序启动时,只有一个goroutine来调用main函数,称为主gourounie;
新的goroutine通过go语句创建。语法上,在普通函数或方法调用前加上go关键字;
f() //调用f(),等待返回
go f() //新建一个调用f()的goroutine,不用等待
main函数返回,所有的goroutine直接终结,程序退出;
没有程序方法让一个gorounie来停止另一个;
有办法和goroutine通信来要求它自己停止;
func main(){
listenser, err := net.Listen("tcp", "localhost:8080")
if err != nil{
log.Fatal(err)
}
for{
conn, err := linstenser.Accept()
if err != nil{
log.Println(err) //连接终止
continue
}
go handleConn(conn) //并发处理
}
}
channel
groroutine是并发的执行体,通道是他们之间的连接;
通道是让一个goroutine发送特定值到另一个goroutine的通信机制;
每一个通道是一个具体类型的导管,叫做通道的元素类型。
一个int类型元素的通道写作 chan int.
初始化
使用内置make函数来创建通道:
ch := make(chan int) //ch的类型是chan int, 无缓冲通过
ch := make(chan int, 0) //无缓冲通道
ch := make (chan int, 3) //容量为3的缓冲通道
通道是一个使用make创建的数据结构的引用。
当复制或作为一个参数传递到一个函数,复制的是引用,调用者和被调用者都引用同一份数据结构。
通道的零值是nil.
同种类型的通道可以使用==符号比较,当二者是同一通道数据的引用时,值为true.也可以和nil比较。
通道的两个操作
send语句从一个goroutine传输一个值到另一个执行接收的goroutine.
发送(send): ch <- x //发送语句
接收(receive) : x = <- ch //赋值语句中的接收表达式
<- ch //接收语句,丢弃结果
第三个操作:close(ch)
设置一个标志位来指示当前已经发送完毕,这个通道没值了。
在一个已经关闭的通过上进行接收操作,将获取所有已发送的值,直到通过为空。
通道类型
无缓冲通道:不带缓存的channels
管道,pipeline: 串联的channels
单向通道:单方向的channel
缓冲通道:带缓存的channels
无缓冲通道
无缓冲即同步,接收值后发送方goroutine才再次唤醒。
并发:不一定同时发生,但不能确定发生的先后顺序;
串行:有一个依赖关系,发生的时间先后顺序固定;
并行:可以在同一个时间发生;
func main(){
conn, err := net.Dial("tcp", "localhost:8080")
checkError(err)
done := make(chan struct{})
go func(){
io.Copy(os.stdout, conn)
log.Println("done")
done <- struct{}{} //指示主goroutine
}()
mustCopy(conn, os.stdin)
conn.close()
<- done //等待后台goroutine完成
}
管道 pipeline
counter————> square ————> printer
0,1,2,3... 0,1,4,9...
如果发送方知道没有更多数据,告诉接收者所在goroutine停止等待是有效的,close(chan)
通道关闭后,任何后续的发送操作将导致应用崩溃。
当关闭的通道被读完,所有的后续的接收操作仍可进行,只是获取到的是零值。
func main(){
naturals := make(chan int)
squares := make(chan int)
//counter
go func(){
for x := 0; x < 100; x++{
naturals <- x
}
close(naturals)
}()
//square
go func(){
for x := range natruals{
square <- x*x
}
close(square)
}()
//printer 在主goroutine中
for x := range squares{
fmt.println(x)
}
}
单向通道
chan <- int: 只能发送;
<- chan int: 只能接收;
func counter (out chan <- int){
for x:= 0; i < 100; x++{
out <- x
}
close(out)
}
func squarer (out chan <- int, in <- chan int){
for v := range in{
out <- v:v
}
close(out)
}
func printer (in <- chan int){
for v := range in{
fmt.Println(v)
}
}
//任何赋值操作将双向通道转换为单向通道都允许的,反过来不行
func main(){
naturals := make(chan int)
squares := make(chan int)
go counter(naturals)
go squares(squares, naturals)
printer(squares)
}
缓冲通道
一个有容量的先进先出队列;
组装流水线是对通道和goroutine合适的比喻;
如果通道已满,对于它的所有发送操作会阻塞,直到通道中所有的元素被取走。此时,通道会优先通知最早等待发送的goroutine,再次执行发送操作。
如果通道为空,对于它的接收操作会被阻塞,直到通道中有新元素出现。此时,通道会优先通知最早等待接收的goroutine,并使它再次执行接收操作。