【问题标题】:Go channels seem not to be blocking although supposed to beGo 频道似乎没有被阻塞,尽管应该是
【发布时间】:2020-04-23 09:58:13
【问题描述】:

我是 golang 新手,很难理解渠道的工作原理。我的理解是,默认情况下,通道应该是阻塞的,所以我希望一个写入通道的 goroutine 被调度程序冻结,直到另一个 goroutine 读取通道内容。所以我尝试了以下代码,它给出了相应的输出:

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup
var v int

func main() {
    ch := make(chan string)
    wg.Add(2)
    go func(ch chan string) {
        fmt.Println("Ready to receive")
        for msg := range ch {
            fmt.Println("received: ", msg)
            fmt.Println(v)
        }
        wg.Done()
    }(ch)
    go func(ch chan string) {
        fmt.Println("Will send the SMS to mama")
        ch <- "msg 1"
        v += 1
        fmt.Println("Done! sent the message 1")
        ch <- "msg 2"
        v += 1
        fmt.Println("Done! sent the message 2")
        ch <- "msg 3"
        v += 1
        fmt.Println("Done! sent the message 3")
        close(ch)
        wg.Done()
    }(ch)

    wg.Wait()
}

输出:

Will send the SMS to mama
Ready to receive
received:  msg 1
0
Done! sent the message 1
Done! sent the message 2
received:  msg 2
2
received:  msg 3
2
Done! sent the message 3

我有点惊讶,因为我期待以下顺序:

  1. 消息 1 已发送
  2. 收到消息 1
  3. 消息 2 已发送
  4. 收到消息 2

等等。但显然情况并非如此。

有人知道为什么 Go 会这样吗?非常感谢,

这里是代码https://play.golang.org/p/O6SXf0CslPf 的链接。以下是我之前所说的话的来源:https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adbhttps://rakyll.org/scheduler/

【问题讨论】:

  • 在通道上发送/接收只能保证发送和接收是同步的。不保证后续打印操作的时间。
  • v 上存在数据竞争。您可以期望发送操作之前的打印语句发生在接收操作之后的打印语句之前,确实如此。
  • 谢谢大家的回答。 @CeriseLimón这是否意味着我们只能保证发送发生在接收操作之前?难道我们不能保证调度程序会在它读取此语句的确切时刻切换到接收者 goroutine,例如ch &lt;- "msg 1"
  • @A.Luc 除非进一步同步,否则任何 goroutine 都可以在无缓冲通道同步后继续(可能同时并行超过 1 个)。
  • @A.Luc 以下是来自memory model 的相关引用:通道上的发送发生在该通道的相应接收完成之前。 没有其他保证关于在通道操作上调度 goroutines。

标签: go channel goroutine


【解决方案1】:

这种行为是完全正常的,所以回答你的问题

所以我希望一个写入通道的 goroutine 被调度程序冻结,直到另一个 goroutine 读取通道内容

除非需要进一步同步,否则在通过通道发送值后,调度可能会或可能不会继续相同的 goroutine。

例如在“msg 2”被发送到ch并在下一行的另一个goroutine中读取之后

ch &lt;- "msg 2"

goroutine 可以继续执行 v += 1 并在其他 goroutine 调用之前调用 fmt.Println

从不同的 goroutine 调用 fmt.Println 也需要同步,并且可能需要 mutex 调用,这也可能重新排序打印语句。

还有变量v上的数据竞争

【讨论】:

    猜你喜欢
    • 2011-05-21
    • 2023-03-11
    • 2021-11-08
    • 1970-01-01
    • 2017-05-23
    • 2021-06-26
    • 1970-01-01
    • 2017-05-08
    • 1970-01-01
    相关资源
    最近更新 更多