登录
首页 >  Golang >  Go问答

为什么这里有通道的可变输出

来源:stackoverflow

时间:2024-04-26 20:54:36 255浏览 收藏

来到golang学习网的大家,相信都是编程学习爱好者,希望在这里学习Golang相关编程知识。下面本篇文章就来带大家聊聊《为什么这里有通道的可变输出》,介绍一下,希望对大家的知识积累有所帮助,助力实战开发!

问题内容

我正在尝试从这里修改代码。我创建 5 个通道并发送数据 5 次:

package main
import "fmt"
func greet(c chan string) {
    fmt.println("hello " + <-c + "!")
}
func main() {
    fmt.println("main() started")
    c := make(chan string)
    for i:=0; i<5; i++ {
        go greet(c)
    }
    c <- "aaa"
    c <- "bbb"
    c <- "ccc"
    c <- "ddd"
    c <- "eee"
    fmt.println("main() stopped")
}

我希望打印所有 5 个字符串。但是,我发现输出可变。一些输出是:

$ ./rnchannel
main() started
Hello AAA!
Hello DDD!
Hello BBB!
Hello CCC!
Hello EEE!
main() stopped

$ ./rnchannel
main() started
Hello CCC!
Hello DDD!
main() stopped

$ ./rnchannel
main() started
Hello CCC!
Hello BBB!
Hello AAA!
Hello DDD!
main() stopped

为什么打印的行数可变?


解决方案


@AdamSmith identified the problem。当 main() 退出时,所有 goroutine 都会被杀死。无法保证您的其他 goroutine 会在此之前完成,这就是并发的本质。以下是修复方法。

首先,让我们对 greet 进行一些更改。让其休眠一会儿,以使问题更加明显。我们还将让它接受字符串而不是通道,稍后我们就会明白原因。

func greet(str string) {
    time.sleep(100 * time.millisecond)
    fmt.println("hello " + str + "!")
}

我们不需要创建一堆 goroutine 从通道读取固定次数的数据,而是需要一个 goroutine 从通道读取数据直到耗尽。使用 range 最容易完成此操作。这充分利用了渠道。

我们还需要一种方法来告诉主程序等待循环完成。 This is easiest done with a second channel。更复杂的同步使用WaitGroups

c := make(chan string, 2)
done := make(chan bool, 1)
go func() {
    for str := range(c) {
        greet(str)
    }
    done <- true
}()

goroutine 将从 c 读取数据,直到它关闭。然后它将发送 true 到通道 done。两者都经过缓冲,以避免由于阻塞等待读取或写入通道而导致死锁。

回到main,我们写入通道,显式关闭它,然后等待从done读取。

c <- "aaa"
    c <- "bbb"
    c <- "ccc"
    c <- "ddd"
    c <- "eee"
    close(c)

    <-done
    fmt.println("main() stopped")

<-done 将阻塞,直到有东西可读。这使得 goroutine 能够完成。

并将它们整合在一起。

package main
import(
    "fmt"
    "time"
)
func greet(str string) {
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Hello " + str + "!")
}
func main() {
    fmt.Println("main() started")
    c := make(chan string, 2)
    done := make(chan bool, 1)
    go func() {
        for str := range(c) {
            greet(str)
        }
        done <- true
    }()
    c <- "AAA"
    c <- "BBB"
    c <- "CCC"
    c <- "DDD"
    c <- "EEE"
    close(c)

    <-done
    fmt.Println("main() stopped")
}

您不会等到所有字符串都打印完才退出。一旦主线程到达执行结束,它就会关闭所有 goroutine 并结束程序。由于这种情况同时发生,因此无法确定将允许打印多少个字符串。

理论要掌握,实操不能落!以上关于《为什么这里有通道的可变输出》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>