先看看无缓冲区域的管道。如下图,
第一步,左边协程产生了一个数据,欲将数据放入管道
第二步,左边协程成功将数据放入管道,但是右边协程不来取,他被锁在管道里了
第三步,右边管道来取数据了
第四步,两个协程完成握手传递数据
第五步,右边管道成功拿到数据
第六步,协程阻塞会被取消,各自执行接下来的流程
代码如下:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var chanS = make(chan string) //定义一个管道变量,不带缓冲区的
go func(a int, b int) {
defer fmt.Println("goroutine结束了")
for i := 0; i <= 20; i++ {
fmt.Printf("%d goroutine a+b=%d\n", i, a+b)
}
chanS <- "channel data" //将goroutine生成的数据传送至管道里,此时会阻塞协程,直到主线程读取管道里的数据。如果主线程不读取管道里的数据的话,协程会一直卡在这里,不会执行下面的语句
func() {
fmt.Printf("内部函数执行\n")
runtime.Goexit() //如果在内部要退出协程的话要用 runtime.Goexit();
}()
fmt.Println("test2") //这个语句不会被执行,因为在上个内部函数里退出了协程
return //使用return退出当前协程
fmt.Println("go")
}(10, 20)
gets := <-chanS //从管道里取出数据,此时会阻塞主线程,直到管道里有数据
fmt.Println("channel data is", gets)
fmt.Println("main goroutine") //由于上面阻塞了线程,所以这里会最后执行
time.Sleep(time.Second)
}
接下来看一下带有缓冲区域的channel
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
chanS := make(chan string, 3) //声明一个容量为3的管道
fmt.Println("管道长度为:", len(chanS), "容量为:", cap(chanS))
go func() {
defer fmt.Println("子goroutine结束了")
for i := 0; i < 4; i++ {
chanS <- "chan " + strconv.Itoa(i)
fmt.Println("子goroutine(", strconv.Itoa(i), ")正在执行,当前长度为:", len(chanS), "容量为:", cap(chanS))
}
}()
time.Sleep(2 * time.Second) //休眠2秒,效果更明显
for i := 0; i < 4; i++ {
str := <-chanS
fmt.Println("主goroutine(", str, ")拿到数据,当前长度为:", len(chanS), "容量为:", cap(chanS))
}
fmt.Println("主goroutine结束了")
}
channel关闭的方法
close(chanS)