【问题标题】:Go synchronization and concurrency modelGo 同步和并发模型
【发布时间】:2019-10-14 01:35:00
【问题描述】:

我是一名新的 Go 学习者,我正在关注 gobyexample.com 以学习基础知识。当我到达“关闭通道”部分时,教程会插入此代码 sn-p(我将删除原始 cmets):

    package main

    import "fmt"

    func main() {
        jobs := make(chan int, 5)
        done := make(chan bool)

       go func() {
           for {
               j, more := <-jobs
               if more {
                   fmt.Println("received job", j)
               } else {
                   fmt.Println("received all jobs")
                   done <- true
                   return
               }
           }
       }()

       for j := 1; j <= 18; j++ {
           jobs <- j
           fmt.Println("sent job", j)
       }
       close(jobs)
       fmt.Println("sent all jobs")

       <-done
    }

原始代码在作业发送者循环中设置了 3 而不是 18。

play.golang.org 中执行此代码是我不完全理解的。它总是输出以下内容:

sent job 1
sent job 2
sent job 3
sent job 4
sent job 5
received job 1
received job 2
received job 3
received job 4
received job 5
received job 6
sent job 6
sent job 7
sent job 8
sent job 9
sent job 10
sent job 11
sent job 12
received job 7
received job 8
received job 9
received job 10
received job 11
received job 12
received job 13
sent job 13
sent job 14
sent job 15
sent job 16
sent job 17
sent job 18
sent all jobs
received job 14
received job 15
received job 16
received job 17
received job 18
received all jobs

所以我知道频道的“队列”(我知道这个术语不是最准确的,但为了了解自己,这是我对频道的理解)大小为 5,所以前 10 条日志消息对我来说很好。

但是消息 6 和 13 怎么能在它们实际发送之前输出它们的接收呢?如果通道大小为 5,如何连续发送 7 条消息?我错过了什么?

【问题讨论】:

    标签: go concurrency synchronization channels


    【解决方案1】:

    Goroutines 并发运行,因此它们也同时打印到标准输出(即无序)。此外,fmt.Print API 是缓冲的,因此您的打印实际上不会在调用时立即打印。这也发生在其他语言中。

    这就是为什么你不能从标准输出中的消息中推导出真正并发执行的 goroutine。

    【讨论】:

    • 我错过了关于“fmt.Print APIs are buffered”的内容。非常感谢
    • fmt.Print 函数在调用时会写入输出。这些函数使用缓冲区来构造输出,但该缓冲区在函数返回之前被写入标准输出。
    • @CeriseLimón,正确。 Go stdlib 不会缓冲到 os.Stdout,但操作系统可能会这样做。我应该说得更好。
    • 缓冲与 OP 观察到的输出顺序无关。关于其他语言的评论:Go 与大多数其他语言的不同之处在于 Go fmt 函数从不缓冲直接写入标准输出的输出。
    【解决方案2】:

    但是消息 6 和 13 怎么能在实际发送之前输出它们的接收呢?

    我们只知道在打印fmt.Println("sent job", j) 之前打印了fmt.Println("received job", j),这在并发工作代码中是合理的。

    如果通道大小为5,如何连续发送7条消息?

    同样,因为我们不确切知道哪个语句首先打印出来,所以这是可以预料的。队列已经可以减少,我们仍在打印。

    【讨论】:

      猜你喜欢
      • 2015-07-09
      • 1970-01-01
      • 2018-02-18
      • 2022-12-10
      • 1970-01-01
      • 2017-01-25
      • 2014-03-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多