V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
liaowb3
V2EX  ›  Go 编程语言

请问大家这里如何打印出“End. [sender]”

  •  
  •   liaowb3 · 278 天前 · 1522 次点击
    这是一个创建于 278 天前的主题,其中的信息可能已经有所发展或是发生改变。

    大家好,这是我的代码:

    func main () {
        // synChan := make(chan struct{}, 1)
        intChan := make(chan int, 1)
        ticker := time.NewTicker(time.Second)
        go func() {
            for range ticker.C {
                select {
                case intChan <- 1:
                case intChan <- 2:
                case intChan <- 3:
                }
            }
            // synChan<- struct{}{}
            fmt.Println("End [SEND]")
        }()
    
        var sum int
    
        for e := range intChan {
            fmt.Printf("接收到  %d\n", e)
            sum += e
            if sum > 3 {
                fmt.Println(sum)
                break
            }
        }
    
        fmt.Println("End [REC]")
    
        // <-synChan
    }
    

    这是《 GO 并发编程》里讲通道的一个题目,但是我试了几个方法之后会成死锁,请问大家如何打印出这个 fmt.Println("End [SEND]")。这里为什么不能打印出来?我想过是 main 函数退出了,所以试着使用通道阻塞,但直接会出死锁。。。

    第 1 条附言  ·  278 天前
    我后面加上了 暂停不管用啊,还是没有打印

    ```go
    func main () {
    // synChan := make(chan struct{}, 1)
    intChan := make(chan int, 1)
    ticker := time.NewTicker(time.Second)
    go func() {
    for range ticker.C {
    select {
    case intChan <- 1:
    case intChan <- 2:
    case intChan <- 3:
    }
    }
    // synChan<- struct{}{}
    fmt.Println("End [SEND]")
    }()

    var sum int

    for e := range intChan {
    fmt.Printf("接收到 %d\n", e)
    sum += e
    if sum > 3 {
    fmt.Println(sum)
    ticker.Stop()
    break
    }
    }

    fmt.Println("End [REC]")

    // <-synChan
    }
    ```
    9 条回复    2024-02-20 09:27:23 +08:00
    sexoutsex2011
        1
    sexoutsex2011  
       278 天前
    我蛮帮你问了大模型,你试试看:

    这段 Go 代码的主要问题是,它在等待一个信号来表明发送 goroutine 已经结束,但是这个信号从未被发送。当`sum`大于 3 时,接收循环会中断,但是发送 goroutine 会因为`intChan`的缓冲区已满而阻塞,导致无法到达打印`"End [SEND]"`的代码行。

    要解决这个问题,你需要确保发送 goroutine 有机会发送完成信号。在这个场景中,你可以通过关闭`intChan`来实现,因为关闭一个通道会导致所有接收者收到一个零值,并且随后的接收都会立即返回零值,而不会阻塞。因为`intChan`是一个 int 类型的通道,接收者会得到一个`0`。

    以下是修正后的代码,它确保了打印出`"End [SEND]"`:

    ```go
    package main

    import (
    "fmt"
    "time"
    )

    func main() {
    intChan := make(chan int, 1)
    ticker := time.NewTicker(time.Second)
    done := make(chan struct{}) // 添加一个完成通道

    go func() {
    for range ticker.C {
    select {
    case intChan <- 1:
    case intChan <- 2:
    case intChan <- 3:
    }
    }
    ticker.Stop() // 停止 ticker
    close(intChan) // 关闭 intChan
    fmt.Println("End [SEND]")
    close(done) // 发送完成信号
    }()

    var sum int

    for e := range intChan {
    fmt.Printf("接收到 %d\n", e)
    sum += e
    if sum > 3 {
    fmt.Println(sum)
    break
    }
    }

    fmt.Println("End [REC]")

    <-done // 等待发送 goroutine 完成
    }
    ```

    这里做了以下改动:

    1. 添加了一个`done`通道,用来接收发送 goroutine 完成的信号。
    2. `ticker.Stop()`被调用来停止 ticker ,避免资源泄露。
    3. 在发送循环结束后关闭`intChan`通道。
    4. 打印`"End [SEND]"`后,关闭`done`通道来发送一个完成信号。
    5. 在主函数的最后,添加`<-done`来等待发送 goroutine 完成。

    现在程序会等待发送 goroutine 结束后,才结束`main`函数,确保`"End [SEND]"`被打印出来。请注意,这个版本的代码假设发送 goroutine 总是会结束,否则可能会导致死锁。在实际编程中,你可能需要添加超时或其他机制来处理这种情况。
    AoEiuV020JP
        2
    AoEiuV020JP  
       278 天前
    @sexoutsex2011 #1 这个 ai 的答案你试过没有,这里是禁止搬运 ai 回答的,
    liaowb3
        3
    liaowb3  
    OP
       278 天前
    @sexoutsex2011 不行,死锁了
    Rehtt
        4
    Rehtt  
       278 天前
    time.NewTicker 是创建了一个周期定时器,下面没有调用 ticker.Stop()来终止定时器,所以 for range ticker.C 回一直循环不会退出
    PTLin
        5
    PTLin  
       278 天前
    你这个不叫死锁好吧,你要在适当的时候调用 ticker.Stop()让这个 Ticker 停下来,让 channel 被 close
    Kisesy
        6
    Kisesy  
       278 天前
    你接收完数据了,不向发送方发送结束命令,它还在发数据呢
    Great233
        7
    Great233  
       278 天前
    ticker.Stop() 停止计时器后只是不再发送数据,但是 channel 并没有被关闭
    https://pkg.go.dev/time#Ticker.Stop

    ```
    func main() {
    intChan := make(chan int, 1)
    ticker := time.NewTicker(time.Second)
    end := make(chan int)
    go func() {
    loop:
    for range ticker.C {
    select {
    case intChan <- 1:
    case intChan <- 2:
    case intChan <- 3:
    case <-end:
    fmt.Println("End [SEND]")
    ticker.Stop()
    break loop
    }
    }
    }()

    var sum int

    for e := range intChan {
    fmt.Printf("接收到 %d\n", e)
    sum += e
    if sum > 3 {
    fmt.Println(sum)
    end <- 1
    break
    }
    }
    fmt.Println("End [REC]")
    }
    ```
    liaowb3
        8
    liaowb3  
    OP
       278 天前
    @Great233 soga ,我懂了,谢谢大佬
    sexoutsex2011
        9
    sexoutsex2011  
       278 天前
    @AoEiuV020JP 抱歉抱歉
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2420 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 02:07 · PVG 10:07 · LAX 18:07 · JFK 21:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.