通道阻塞顺序
Order of channel blocking
我想了解通道在 golang 中的工作原理。我的代码非常简单,但输出却令人惊讶。
如文档所述:读取和写入 from/to 一个通道正在阻塞当前的 goroutine,所以我认为写入一个通道会阻塞该通道,直到主例程产生。
package main
func rtn(messages chan<- string) {
defer close(messages)
println("p1")
messages <- "ping1"
//for i := 0; i < 10000000; i++ { }
println("p2")
messages <- "ping2"
}
func main() {
messages := make(chan string)
go rtn(messages)
for msg := range messages {
println(msg)
}
}
我以为它会打印出来
p1
ping1
p2
ping2
但它实际上打印
p1
p2
ping1
ping2
您正在使用无缓冲通道,它作为主协程和第二协程之间的同步点。
在这种情况下,您只知道当第二个 goroutine 在这里时 messages <- "ping1"
主要的 goroutine 在 for msg := range messages
行。因此,不能保证主循环会立即到达 println(msg)
。也就是说,与此同时,第二个 goroutine 可以继续前进并到达行 println("p2")
和 messages <- "ping2"
.
作为反例,我添加一个通道只是为了强制打印之间的完全同步。
package main
func rtn(messages chan<- string, syncChan chan struct{}) {
defer close(messages)
println("p1")
messages <- "ping1"
//Wait for main goroutine to print its message
<-syncChan
//for i := 0; i < 10000000; i++ { }
println("p2")
messages <- "ping2"
//Wait for main goroutine to print its message
<-syncChan
}
func main() {
messages := make(chan string)
syncChan := make(chan struct{})
go rtn(messages, syncChan)
for msg := range messages {
println(msg)
//Notify the second goroutine that is free to go
syncChan <- struct{}{}
}
}
打印出您期望的输出:
p1
ping1
p2
ping2
这是生成您正在寻找的输出的另一个示例。在这种情况下,主 goroutine 被 time.Sleep()
强制阻塞。这将使第二个 goroutine 在接收器准备好接收之前准备好发送。因此,发送方实际上会阻塞发送操作。
package main
import (
"time"
)
func rtn(messages chan<- string) {
defer close(messages)
println("p1")
messages <- "ping1"
//for i := 0; i < 10000000; i++ { }
println("p2")
messages <- "ping2"
}
func main() {
messages := make(chan string)
go rtn(messages)
//Put main goroutine to sleep. This will make the
//sender goroutine ready before the receiver.
//Therefore it will have to actually block!
time.Sleep(time.Millisecond * 500)
for msg := range messages {
println(msg)
}
}
我想了解通道在 golang 中的工作原理。我的代码非常简单,但输出却令人惊讶。
如文档所述:读取和写入 from/to 一个通道正在阻塞当前的 goroutine,所以我认为写入一个通道会阻塞该通道,直到主例程产生。
package main
func rtn(messages chan<- string) {
defer close(messages)
println("p1")
messages <- "ping1"
//for i := 0; i < 10000000; i++ { }
println("p2")
messages <- "ping2"
}
func main() {
messages := make(chan string)
go rtn(messages)
for msg := range messages {
println(msg)
}
}
我以为它会打印出来
p1
ping1
p2
ping2
但它实际上打印
p1
p2
ping1
ping2
您正在使用无缓冲通道,它作为主协程和第二协程之间的同步点。
在这种情况下,您只知道当第二个 goroutine 在这里时 messages <- "ping1"
主要的 goroutine 在 for msg := range messages
行。因此,不能保证主循环会立即到达 println(msg)
。也就是说,与此同时,第二个 goroutine 可以继续前进并到达行 println("p2")
和 messages <- "ping2"
.
作为反例,我添加一个通道只是为了强制打印之间的完全同步。
package main
func rtn(messages chan<- string, syncChan chan struct{}) {
defer close(messages)
println("p1")
messages <- "ping1"
//Wait for main goroutine to print its message
<-syncChan
//for i := 0; i < 10000000; i++ { }
println("p2")
messages <- "ping2"
//Wait for main goroutine to print its message
<-syncChan
}
func main() {
messages := make(chan string)
syncChan := make(chan struct{})
go rtn(messages, syncChan)
for msg := range messages {
println(msg)
//Notify the second goroutine that is free to go
syncChan <- struct{}{}
}
}
打印出您期望的输出:
p1
ping1
p2
ping2
这是生成您正在寻找的输出的另一个示例。在这种情况下,主 goroutine 被 time.Sleep()
强制阻塞。这将使第二个 goroutine 在接收器准备好接收之前准备好发送。因此,发送方实际上会阻塞发送操作。
package main
import (
"time"
)
func rtn(messages chan<- string) {
defer close(messages)
println("p1")
messages <- "ping1"
//for i := 0; i < 10000000; i++ { }
println("p2")
messages <- "ping2"
}
func main() {
messages := make(chan string)
go rtn(messages)
//Put main goroutine to sleep. This will make the
//sender goroutine ready before the receiver.
//Therefore it will have to actually block!
time.Sleep(time.Millisecond * 500)
for msg := range messages {
println(msg)
}
}