1. 最近看到一个小例子,简单分析了一下函数是如何退出的,copy下代码跑了一下和自己起初想的不太一样,所以再次记录一下以免忘记。如有误请指正,代码如下:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
generator := func(done <-chan interface{}, integers ...int) <-chan int {
intStream := make(chan int)
go func() {
defer close(intStream)
for _, i := range integers {
select {
case <-done:
return
case intStream <- i:
}
}
}()
return intStream
}
multiply := func(done <-chan interface{}, intStream <-chan int, multiplier int) <-chan int {
multipliedStream := make(chan int)
go func() {
defer close(multipliedStream)
for i := range intStream {
select {
case <-done:
return
case multipliedStream <- i * multiplier:
}
}
}()
return multipliedStream
}
add := func(done <-chan interface{}, intStream <-chan int, additive int) <-chan int {
addedStream := make(chan int)
go func() {
defer close(addedStream)
for i := range intStream {
select {
case <-done:
return
case addedStream <- i + additive:
}
}
}()
return addedStream
}
done := make(chan interface{})
defer close(done)
intStream := generator(done, 1, 2, 3, 4)
pipeline := multiply(done, add(done, multiply(done, intStream, 2), 1), 2)
for v := range pipeline {
fmt.Println(v)
}
}
2.第一感觉是使用main函数的done结束的各个goroutine的,加一点打印看一下,在每个函数的select里加入一行打印信息。
......
......
case <-done:
fmt.Println("generator done...")
......
看一下输出结果:
并没有打印新添加的信息,难道是goroutine没有退出? 加一段代码查看当前goroutine的变化。
......
intStream := generator(done, 1, 2, 3, 4)
pipeline := multiply(done, add(done, multiply(done, intStream, 2), 1), 2)
count := runtime.NumGoroutine()
fmt.Printf("计算前运行的goroutine数量:%d\n", count)
go func() {
time.Sleep(2 * time.Second)
close(done)
}()
for v := range pipeline {
fmt.Println(v)
}
count = runtime.NumGoroutine()
fmt.Printf("计算后运行的goroutine数量:%d\n", count)
......
......
看一下输出:
“计算后运行的goroutine数量:1” 说明开启的4个goroutine已经退出了,那么是怎么退出的那?
再次仔细看看代码发现generator的第二个参数是一个可变参数,内部当做切片使用,range 遍历传入的切片数据,当遍历完所有数据后
退出for循环也就退出了generator中的觅名函数,此时调用的defer close(intStream)这个chan ,而multiply函数依赖这个chan
当generator中的chan关闭后multiply中的 range 也结束了for循环 函数退出,退出时关闭了 multipliedStream这个chan,
add函数依赖multipliedStream这个chan 也就随之关闭,在代码中打印的东西印证一下。
每个函数的for后边添加一行打印。
......
......
go func() {
defer close(intStream)
//defer fmt.Println("generator def done....")
for _, i := range integers {
//fmt.Printf("rge %d \n", i)
select {
case <-done:
fmt.Println("generator done...")
return
case intStream <- i:
}
}
fmt.Println("generator 结束for循环")
}()
......
看一下输出:
那done这个chan可以提前结束计算吗?加点东西看看。
在开启的goroutine中加入一秒的延时。
在main函数中加如代码
count := runtime.NumGoroutine()
fmt.Printf("计算前运行的goroutine数量:%d\n", count)
go func() {
time.Sleep(2 * time.Second)
close(done)
}()
for v := range pipeline {
fmt.Println(v)
}
count = runtime.NumGoroutine()
fmt.Printf("计算后运行的goroutine数量:%d\n", count)
......
......
有疑问加站长微信联系(非本文作者)