go buffered channel (capacity 4) 接受 5 不阻塞
go buffered channel (capacity 4) accepts 5 without blocking
我有 go 代码,它基本上同时运行两个 go 例程。其中一个将 10 个 int 数字从 (1-10) 发送到缓冲通道“ch”(容量为 4),另一个 go 例程借助 for range loop
从通道读取值
package main
import (
"fmt"
"sync"
"time"
"runtime"
)
func doSm(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 1; i <= 10; i++ {
fmt.Println("sending", i)
ch <- i
fmt.Println("sent", i)
}
close(ch)
}
func doSm2(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(5 * time.Second)
for v := range ch {
fmt.Println("result:", v)
}
}
func main() {
runtime.GOMAXPROCS(1)
var wg sync.WaitGroup
ch := make(chan int, 4)
wg.Add(2)
go doSm(ch, &wg)
go doSm2(ch, &wg)
wg.Wait()
}
问题可以在下面的输出中找到。
doSm() 通过通道发送 4 个 int 并且 go 调度程序阻塞 go 例程,直到 doSm2() 从通道读取这 4 个值。之后,缓冲区为空,doSm() 发送 1 个 int,doSm2() 立即读取它。现在,缓冲区再次为空并准备发送 4 个值。然而,doSm() 以某种方式发送了 5 个值(6、7、8、9、10),尽管它有容量。
sending 1
sent 1
sending 2
sent 2
sending 3
sent 3
sending 4
sent 4
sending 5
result: 1
result: 2
result: 3
result: 4
result: 5
sent 5
sending 6
sent 6
sending 7
sent 7
sending 8
sent 8
sending 9
sent 9
sending 10
sent 10
result: 6
result: 7
result: 8
result: 9
result: 10
知道为什么会这样吗?还是我遗漏了什么?
下面是可能会导致此输出的执行:
- 发送goroutine发送4个值,并打印
- 正在发送 goroutine 块
- 接收goroutine接收一个值
- 发送 goroutine 发送另一个值并打印
- 接收协程打印
- 接收goroutine接收剩余值
通过这种交错,发送 goroutine 似乎发送了 5 个值。
简而言之:当涉及多个 goroutine 时,打印到 stdout 可能不会揭示 goroutine 的实际交错。
举例说明。
result: 5
sent 5
sending 6
sent 6
sending 7
sent 7
sending 8
sent 8
sending 9
sent 9
sending 10
sent 10
result: 6
result 5
被打印,然后我们看到 sent 6-7-8-9-10
,然后我们看到 result 6
被打印。这并不意味着值 6-7-8-9-10
都在通道的缓冲区中(显然它们不是)。值 6
已从通道接收,但后续 fmt.Println()
行尚未执行。但是由于收到 6
,缓冲区中只有 3 个数字,因此可以在通道上发送 10
,如输出所示。
我有 go 代码,它基本上同时运行两个 go 例程。其中一个将 10 个 int 数字从 (1-10) 发送到缓冲通道“ch”(容量为 4),另一个 go 例程借助 for range loop
package main
import (
"fmt"
"sync"
"time"
"runtime"
)
func doSm(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 1; i <= 10; i++ {
fmt.Println("sending", i)
ch <- i
fmt.Println("sent", i)
}
close(ch)
}
func doSm2(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(5 * time.Second)
for v := range ch {
fmt.Println("result:", v)
}
}
func main() {
runtime.GOMAXPROCS(1)
var wg sync.WaitGroup
ch := make(chan int, 4)
wg.Add(2)
go doSm(ch, &wg)
go doSm2(ch, &wg)
wg.Wait()
}
问题可以在下面的输出中找到。
doSm() 通过通道发送 4 个 int 并且 go 调度程序阻塞 go 例程,直到 doSm2() 从通道读取这 4 个值。之后,缓冲区为空,doSm() 发送 1 个 int,doSm2() 立即读取它。现在,缓冲区再次为空并准备发送 4 个值。然而,doSm() 以某种方式发送了 5 个值(6、7、8、9、10),尽管它有容量。
sending 1
sent 1
sending 2
sent 2
sending 3
sent 3
sending 4
sent 4
sending 5
result: 1
result: 2
result: 3
result: 4
result: 5
sent 5
sending 6
sent 6
sending 7
sent 7
sending 8
sent 8
sending 9
sent 9
sending 10
sent 10
result: 6
result: 7
result: 8
result: 9
result: 10
知道为什么会这样吗?还是我遗漏了什么?
下面是可能会导致此输出的执行:
- 发送goroutine发送4个值,并打印
- 正在发送 goroutine 块
- 接收goroutine接收一个值
- 发送 goroutine 发送另一个值并打印
- 接收协程打印
- 接收goroutine接收剩余值
通过这种交错,发送 goroutine 似乎发送了 5 个值。
简而言之:当涉及多个 goroutine 时,打印到 stdout 可能不会揭示 goroutine 的实际交错。
举例说明。
result: 5
sent 5
sending 6
sent 6
sending 7
sent 7
sending 8
sent 8
sending 9
sent 9
sending 10
sent 10
result: 6
result 5
被打印,然后我们看到 sent 6-7-8-9-10
,然后我们看到 result 6
被打印。这并不意味着值 6-7-8-9-10
都在通道的缓冲区中(显然它们不是)。值 6
已从通道接收,但后续 fmt.Println()
行尚未执行。但是由于收到 6
,缓冲区中只有 3 个数字,因此可以在通道上发送 10
,如输出所示。