为什么这些goroutines的WaitGroup没有正常工作?

时间:2018-04-12 06:33:28

标签: go concurrency channel

此代码适用于我的编程语言类的相当简单的演示。我试图显示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)


}

1 个答案:

答案 0 :(得分:7)

你在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)
        }
    }
}()

此外,两个功能都试图从完成接收。读者应该关闭频道而不是表示完成:

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中的频道。频道应始终在发送方侧关闭。

完成所有这些后,您应该认识到该消息已完成并且已完成。整个程序可以简化为:

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)
    }
}