Go中使用channel控制goroutine退出

s_k6688 · · 1851 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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)

......

......


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1851 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传