71

Go中使用channel控制goroutine退出

 5 years ago
source link: https://studygolang.com/articles/19201?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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)

......

......

看一下输出:

M3AfAbY.png!web

“计算后运行的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循环")

}()

......

看一下输出:

FNZZFj2.png!web

那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)

......

......

Ffq2yar.png!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK