【问题标题】:Why aren't these goroutines' WaitGroups working correctly?为什么这些 goroutine 的 WaitGroup 不能正常工作?
【发布时间】:2018-04-12 06:33:27
【问题描述】:

此代码用于我的编程语言类的一个相当简单的演示。我试图展示一些 Go 允许的不同技术,比如接口和并发,但我似乎无法让 WaitGroups 正常工作,所以最后它一直让我陷入僵局。我最大的问题是:当 goroutines 停止时,如何让 WaitGroups 正确同步而不使系统死锁?我很可能遗漏了一些明显的东西。

package main

import (
    "bufio"
    "fmt"
    "os"
    "sync"
)


func Reader(wg *sync.WaitGroup, message chan string, done chan bool){
defer wg.Done()
        reader := bufio.NewReader(os.Stdin)
        for {

            msg, _ := reader.ReadString('\n')
            if msg == "exit\n" {
                <-done
                return
            } else {
                message <- msg
            }

        }

 }

func main() {
    message := make(chan string)
    done := make(chan bool)
    wg := &sync.WaitGroup{}


    wg.Add(1)
    go Reader(wg, message, done) 


    wg.Add(1)
    go func(){
    defer wg.Done()
        for {
            select {
            case <-done:
                return
            case msg := <-message:
                fmt.Println("main: "+msg)
            }
        }

    }() 

    wg.Wait()
    close(message)
    close(done)


}

【问题讨论】:

  • 我会删除done 频道并关闭message

标签: go concurrency channel


【解决方案1】:

main 中的 break 语句会中断 select,而不是 for 循环。请改用 return 或 label

go func() {
    defer wg.Done()
    for {
        select {
        case <-done:
            return // don't break here without label
        case msg := <-message:
            fmt.Println("main: " + msg)
        }
    }
}()

此外,这两个函数都尝试从 done 接收。读者应该关闭频道以表示完成:

func Reader(wg *sync.WaitGroup, message chan string, done chan bool) {
        defer wg.Done()
        defer close(done) // close channel to signal completion

        reader := bufio.NewReader(os.Stdin)
        for {
                msg, _ := reader.ReadString('\n')
                if msg == "exit\n" {
                        return
                } else {
                        message <- msg
                }
        }
}

不要关闭 main 中的通道。通道应始终在发送方关闭。

完成所有操作后,您应该认识到该消息和 done 是多余的。整个程序可以简化为:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func Reader(message chan string) {
    defer close(message)

    reader := bufio.NewReader(os.Stdin)
    for {
        msg, _ := reader.ReadString('\n')
        if msg == "exit\n" {
            return
        } else {
            message <- msg
        }
    }
}

func main() {
    message := make(chan string)
    go Reader(message)

    for msg := range message {
        fmt.Println("main: " + msg)
    }
}

【讨论】:

  • 我对上面的代码库进行了快速修改,以使问题更易于阅读和修改,但您的解决方案不起作用。告诉我为什么这段代码仍然会导致死锁。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-23
  • 2021-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-30
相关资源
最近更新 更多