002 Golang-channel-practice

第二题:

创建一个生产器和接收器,再建立一个无缓冲的channel。生产器负责把数据放进管道里,接收器负责把管道里面的数据打印出来。这里我们开5个协程把数据打印出来。

直接上代码!

package main

import (
    "fmt"
)

func receive(c <-chan int) {
    /*for v := range c {
    fmt.Println("received:", v)
    }*/
    for i := 0; i <= 1; i++ {
        go func() {
           for v := range c {
              fmt.Println(v)
           }
        }()
    }
}
func generator() <-chan int {
    c := make(chan int)
    for i := 0; i <= 9; i++ {
       go func(i int) {
          for j := 0; j <= 9; j++ {
             temp := i*100 + 20 + j
             c <- temp
          }
          close(c)
       }(i)
    }
    return c
}
func main() {
    c := generator()
    receive(c)
}

埋了个小坑,跑上面的代码,在这里是不会有任何输出的。

87349339b46d4558b9a0c71f976ee5a4.png

原因是main函数结束时程序就退出了,没有给goroutine足够运行的时间来打印输出。

整个流程是并发执行的,main函数、generator的goroutine、receive的goroutine都是并发运行。

但是问题是main函数和generator很快就结束了,程序退出,receive的goroutine来不及打印数据。

解决方法就是让main函数等一等receive的goroutine。我们在main函数中加上一句:

time.Sleep(time.Second * 5) 

这时看到可以顺利输出了。

但是...

f0e9170ff1914f46baf6820c0b15c56c.png

但是却panic了。为什么呢?

因为generator()把消息发送到了关闭的管道。是因为生成器goroutine和接收goroutine的生命周期没有控制好导致的。

主要原因在于,接收的goroutine一旦从通道接收完所有的数据并退出,通道就会被关闭。

而此时,生成器goroutine可能还在向这个通道发送数据,于是产生了panic。

要避免这种情况,需要确保:

 

1、接收goroutine在最后一个生成器goroutine退出之前不能退出。

2、生成器goroutine在关闭通道之前,必须保证接收goroutine仍在运行。

 

问题出在生成器中close(c)这一行。这里每个goroutine都在自己完成后关闭了通道c。

按照程序逻辑,通道c应该在最后一个goroutine完成时关闭一次,而不是每个goroutine都关闭。所以应该只在主goroutine中关闭c。这里我们用WaitGroup来同步。

 

func generator() <-chan int {
    c := make(chan int) 
    var wg sync.WaitGroup
    wg.Add(10) // 添加10个goroutine
    for i := 0; i < 10; i++ {
        go func() {
            // 生成数据 
            wg.Done() // goroutine结束
        }()
    } 
    go func() {
       wg.Wait()   // 等待所有goroutine完成
       close(c) // 关闭通道,仅关闭一次 
    }()
    return c 
}

 

顺利输出!!

e5ee8c796d6e454c9e30b504158688a3.png

 

 

相关推荐

  1. 006 Golang-channel-practice 并发打印字符串

    2024-01-12 18:12:02       56 阅读
  2. golang channel

    2024-01-12 18:12:02       37 阅读
  3. golang学习-channel管道

    2024-01-12 18:12:02       47 阅读
  4. Golang 并发 Channel的用法

    2024-01-12 18:12:02       34 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-12 18:12:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-12 18:12:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-01-12 18:12:02       82 阅读
  4. Python语言-面向对象

    2024-01-12 18:12:02       91 阅读

热门阅读

  1. HarmonyOS应用开发者高级认证题库

    2024-01-12 18:12:02       61 阅读
  2. Excel办公--常见快捷指令

    2024-01-12 18:12:02       47 阅读
  3. Mybatis 分页

    2024-01-12 18:12:02       49 阅读
  4. #Uniapp:uni-app加载全局组件的方法easycom

    2024-01-12 18:12:02       60 阅读
  5. springboot 集成 @Cacheable简单示例

    2024-01-12 18:12:02       55 阅读
  6. 平方朋友对C++

    2024-01-12 18:12:02       51 阅读
  7. Jenkins相关问题及答案(2024)

    2024-01-12 18:12:02       45 阅读