假设一个 goroutine 在两个 unbuffered channel one
和 two
上等待以下选择
select {
case <-one:
fmt.Println("read from one")
case <-two:
fmt.Println("read from two")
}
一个一个的goroutine正在等待下面的发送
one <- 1
另一个正在等待下面
two <- 2
第一个等待选择意味着缓冲区中有空间用于 channel one
和 two
,然后是 select
情况保证运行?它是确定性的还是可以在一个 channel 最后留下一个未读值的情况下运行。
如果只有一个保证的净输出,那么 select
是否确保参与 select
的所有 channel 上的所有操作的总顺序?这看起来效率很低..
例如在下面的代码中
package main
import (
"fmt"
"time"
"sync"
)
func main() {
one_net := 0
two_net := 0
var mtx = &sync.Mutex{}
for i := 0; i < 8; i++ {
one, two := make(chan int), make(chan int)
go func() { // go routine one
select {
case <-one:
fmt.Println("read from one")
mtx.Lock()
one_net++
mtx.Unlock()
case <-two:
fmt.Println("read from two")
mtx.Lock()
two_net++
mtx.Unlock()
}
}()
go func() { // go routine two
one <- 1
mtx.Lock()
one_net--
mtx.Unlock()
fmt.Println("Wrote to one")
}()
go func() { // go routine three
two <- 2
mtx.Lock()
two_net--
mtx.Unlock()
fmt.Println("Wrote to two")
}()
time.Sleep(time.Millisecond)
}
mtx.Lock()
fmt.Println("one_net", one_net)
fmt.Println("two_net", two_net)
mtx.Unlock()
}
读取次数与写入次数是否会不匹配(即 one_net
和 two_net
最后是否可以为非 0)?例如,如果 select 语句正在等待来自两个 channel 的读取,然后 goroutines 2 和 3 通过它们各自的写入,但是 select
只接受其中一个写入.
最佳答案
The Go Programming Language Specification
A "select" statement chooses which of a set of possible send or receive operations will proceed.
If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection.
您的问题不准确:How to create a Minimal, Complete, and Verifiable example.例如,
chan.go
:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println()
for i := 0; i < 8; i++ {
one, two := make(chan int), make(chan int)
go func() { // goroutine one
select {
case <-one:
fmt.Println("read from one")
case <-two:
fmt.Println("read from two")
}
select {
case <-one:
fmt.Println("read from one")
case <-two:
fmt.Println("read from two")
}
fmt.Println()
}()
go func() { // goroutine two
one <- 1
}()
go func() { // goroutine three
two <- 2
}()
time.Sleep(time.Millisecond)
}
}
输出:
$ go run chan.go
read from two
read from one
read from one
read from two
read from one
read from two
read from two
read from one
read from one
read from two
read from two
read from one
read from one
read from two
read from two
read from one
$
您期望什么行为,为什么?
The Go Programming Language Specification
A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type.
A new, initialized channel value can be made using the built-in function make, which takes the channel type and an optional capacity as arguments:
make(chan int, 100)
The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives). A nil channel is never ready for communication.
A "go" statement starts the execution of a function call as an independent concurrent thread of control, or goroutine, within the same address space.
The function value and parameters are evaluated as usual in the calling goroutine, but unlike with a regular call, program execution does not wait for the invoked function to complete. Instead, the function begins executing independently in a new goroutine. When the function terminates, its goroutine also terminates. If the function has any return values, they are discarded when the function completes.
分析您的新示例:
channel 是无缓冲的。 Goroutine 2 和 3 在 Goroutine 1 上等待。无缓冲 channel 上的发送会一直等待,直到有待处理的接收。当 goroutine one select 被评估时, channel 1 或 channel 2 上将有一个待处理的接收。在该 channel 上发送的两个或三个 goroutine 现在可以发送和终止。 Goroutine one 现在可以在该 channel 上执行接收并终止。作为一种粗略的 goroutine 同步机制,我们等待 goroutine main 一毫秒然后终止它,这会终止任何其他 goroutine。它将终止两个或三个未发送的 goroutine,因为它仍在等待挂起的接收。
您会问“读取次数与写入次数是否会不匹配(即 one_net 和 two_net 最后是否可以为非 0)?例如,在 select 语句等待读取的情况下来自两个 channel ,然后 goroutines 2 和 3 通过它们各自的写入,但随后 select 只选择其中一个写入。”
只有 goroutines 2 和 3 之一可以发送(写入)。将恰好有一个(发送)写入和一个(接收)读取。这假设 goroutine main 在这发生之前没有终止,也就是说,它在一毫秒内发生。
关于select - 选择中同时发生多个事件时的预期行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44745406/