Golang基础通道示例:问题排查与解决

Golang基础通道示例:问题排查与解决

func main() {
    c := make(chan int, 1)
    go send(c)
    go receive(c)
}

// send channel
func send(c chan<- int) {
    for i := 0; i < 10; i++ {
        c <- i
    }
    //close(c)               //  dont have to do it , this is not logical.
}

// receive channel
func receive(c <-chan int) {
    for v := range c {                                                                              // this is an endless loop that will keep running once the channel will have content.
        fmt.Println("Received from the channel:", v)                    this loop will stop only in case the channel will get closed. 
    }
}

我在这里遗漏了什么概念,为什么它没有显示任何输出? 当我添加 close(c) 时,它可以工作,但我不想关闭任何通道——我希望它们保持打开状态以供后续使用。

提前感谢。 @) #channels #go 通道 #请添加哈希标签 - 我认为这在这里会有所帮助。


更多关于Golang基础通道示例:问题排查与解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

是的,你需要关闭通道,因为 range 循环会持续从通道读取数据,直到通道被关闭。

换句话说,关闭通道就像是向 range 操作符发送一个信号,让它知道不会有更多的数据通过该通道发送过来了。

更多关于Golang基础通道示例:问题排查与解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好 @edgarlip

在启动了两个 goroutine 之后,main 函数就无事可做并退出了。当它退出时,整个进程也随之退出,所有 goroutine 都会被强制停止。receive() 函数根本没有机会打印任何内容。

以下是几种避免这种情况的方法:

  1. 快速修复:直接调用 receive()(即移除 go 关键字),使其在主流程中运行。
  2. 对于更复杂的场景:将所有 goroutine 添加到一个 WaitGroup 中,并让 main 函数等待该组完成。
  3. 临时方案:在 main 函数的末尾,添加一个永久阻塞的调用,例如 select{}。然后使用 Ctrl-C 退出应用程序。

@christophberger 感谢您的快速回复! 根据您的评论,我编写了以下代码:

func main() {
	c := make(chan int, 12)
	var wg sync.WaitGroup
	wg.Add(2)
	go send(c, &wg)
	go receive(c, &wg)
	wg.Wait()
}

// send channel
func send(c chan int, wg *sync.WaitGroup) {
	for i := 0; i < 10; i++ {
		fmt.Println("inserting stuff to channel c ", i)
		c <- i
	}
	fmt.Println("send func finished")
	close(c)
	wg.Done()
}

// receive channel
func receive(c chan int, wg *sync.WaitGroup) {
	for v := range c {
		fmt.Println("the value received from the channel:", v)
	}
	fmt.Println("receive func finished")
	wg.Done()
}

但是,正如您在发送函数中看到的,close() 是必须的吗?

感谢 @christophberger 我理解了您的回答,它对于消除不确定性非常有帮助。 我将在这里添加我认为能够体现无限等待某些内容这一概念的最终代码,其中使用了您提到的“select”功能 = )

func main() {
    even := make(chan int)
    odd := make(chan int)
    quit := make(chan bool)
    var wg sync.WaitGroup
    wg.Add(2)

    go send2(even, odd, quit, &wg)
    go receive2(even, odd, quit, &wg)

    wg.Wait()
    fmt.Println("*** done *** ")
}

// send channel
func send2(even chan int, odd chan int, quit chan bool, wg *sync.WaitGroup) {
    for i := 0; i < 10; i++ {
        if i%2 == 0 {
            even <- i
        } else {
            odd <- i
        }
    }
    quit <- true
    wg.Done()
}

// receive channel
func receive2(even, odd chan int, quit chan bool, wg *sync.WaitGroup) {
    for {
        select {
        case v := <-even:
            fmt.Println("the value received from the even channel:", v)
        case v := <-odd:
            fmt.Println("the value received from the odd channel:", v)
        case <-quit:
            fmt.Println("received value on channel quit !")
            wg.Done()
            return
        }
    }
}

此致,

#Golang通道示例问题排查

问题在于主函数main()在启动goroutine后立即退出,导致程序终止,没有给goroutine足够的时间执行。此外,接收循环range会在通道关闭时结束,但发送方没有关闭通道,导致接收方一直等待。

解决方案

方案1:使用WaitGroup等待goroutine完成

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    c := make(chan int, 1)
    
    wg.Add(2)
    go send(c, &wg)
    go receive(c, &wg)
    
    wg.Wait()
}

func send(c chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 10; i++ {
        c <- i
    }
}

func receive(c <-chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 10; i++ {
        v := <-c
        fmt.Println("Received from the channel:", v)
    }
}

方案2:使用带缓冲的通道和明确的接收次数

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int, 10) // 增加缓冲区大小
    
    go send(c)
    go receive(c)
    
    time.Sleep(1 * time.Second) // 给goroutine时间执行
}

func send(c chan<- int) {
    for i := 0; i < 10; i++ {
        c <- i
    }
}

func receive(c <-chan int) {
    for i := 0; i < 10; i++ {
        v := <-c
        fmt.Println("Received from the channel:", v)
    }
}

方案3:使用context控制goroutine生命周期

package main

import (
    "context"
    "fmt"
    "time"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    
    c := make(chan int, 1)
    
    go send(ctx, c)
    go receive(ctx, c)
    
    <-ctx.Done()
}

func send(ctx context.Context, c chan<- int) {
    for i := 0; i < 10; i++ {
        select {
        case c <- i:
            time.Sleep(100 * time.Millisecond)
        case <-ctx.Done():
            return
        }
    }
}

func receive(ctx context.Context, c <-chan int) {
    for {
        select {
        case v := <-c:
            fmt.Println("Received from the channel:", v)
        case <-ctx.Done():
            return
        }
    }
}

关键概念

  1. 主goroutine退出会终止所有其他goroutine
  2. range在通道关闭时才会结束循环
  3. 同步机制(WaitGroup、channel、context)是协调goroutine执行的关键

#Go通道 #并发编程 #goroutine同步

回到顶部