package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan bool, 1)
defer close(ch)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
ch <- true
defer wg.Done()
var nums []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for _, num := range nums {
<-ch
fmt.Print(num, " ")
ch <- true
}
}()
go func() {
defer wg.Done()
var letters []byte = []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
for _, letter := range letters {
<-ch
fmt.Print(string(letter), " ")
ch <- true
}
}()
wg.Wait()
}
上述代码会概率性的打印下述两种结果。为什么会出现这种现象?
a 1 b 2 c 3 d 4 e 5 f 6 g 7 h 8 i 9 j
1 a 2 b 3 c 4 d 5 e 6 f 7 g 8 h 9 i j
相比较上述代码只是将 第 1 个 goroutine 第一行的 ch <- true
移动到了第 2 个 goroutine 中
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan bool, 1)
defer close(ch)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
var nums []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for _, num := range nums {
<-ch
fmt.Print(num, " ")
ch <- true
}
}()
go func() {
ch <- true
defer wg.Done()
var letters []byte = []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
for _, letter := range letters {
<-ch
fmt.Print(string(letter), " ")
ch <- true
}
}()
wg.Wait()
}
上述代码会概率性的打印出下述几种结果。为什么会出现这种现象?
a b c d e f g h i j 1 2 3 4 5 6 7 8 9
a 1 b 2 c 3 d 4 e 5 f 6 g 7 h 8 i 9 j
a b 1 c 2 d 3 e 4 f 5 g 6 h 7 i 8 j 9
如果执行次数比较少,可能不会出现这个现象。但是如果执行上百次,就会出现。
for i in {1..100}; do go run main.go; done
如果我想稳定的先打印数字,再打印字母,应该如何修改?
感谢大家,改成下述写法就正确了。
package main
import (
"fmt"
"sync"
)
func main() {
chN := make(chan bool, 1)
chC := make(chan bool, 1)
defer close(chN)
defer close(chC)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
var nums []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for _, num := range nums {
<-chN
fmt.Print(num, " ")
chC <- true
}
}()
go func() {
chN <- true
defer wg.Done()
var letters []byte = []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'}
for _, letter := range letters {
<-chC
fmt.Print(string(letter), " ")
chN <- true
}
}()
wg.Wait()
fmt.Println()
}
![]() |
1
nagisaushio 51 天前 via Android
用两个 channel
|
2
kirara2024 51 天前
package main
import ( "fmt" "sync" ) func main() { numDone := make(chan bool) wg := sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() defer close(numDone) var nums []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9} for _, num := range nums { fmt.Print(num, " ") } fmt.Println() }() go func() { defer wg.Done() <-numDone var letters []byte = []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'} for _, letter := range letters { fmt.Print(string(letter), " ") } fmt.Println() }() wg.Wait() } |
![]() |
3
dV9zZM1wROuBT16X 51 天前 ![]() goroutine 的运行不是按顺序的,一般是 FILO ,只有饥饿了才会 FIFO ,你还要考虑下其他 M 空闲时窃取 G
|
![]() |
4
rekulas 51 天前 ![]() 这才是符合预期的,你用两个协程打印,就算用了 chan 又如何,你不能保证一个协程提交了数据马上又自己抢占到,这又不是一个原子操作,而且在不同硬件,环境的机器上估计结果也有差距..
要稳定的两个协程打印数字最稳的还是一个加锁,确认打印完成了另一个才拿到锁就行了,或者用协程池的模式顺序执行协程队列 |
![]() |
5
dallaslu 51 天前 ![]() 这是在模拟《功夫》中的拍卖过程吗?按套路来,一人一句,不能喊乱了。顺着 @nagisaushio #1 的思路,用 两个 channel 来捋一捋。
范厨师:你别说话,啊!两千,两千五,三千,三千五,四千,四千五,五千! ```go chN := make(chan bool, 1) chC := make(chan bool, 1) defer close(chN) defer close(chC) wg := sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() var nums []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9} for _, num := range nums { <-chN fmt.Print(num, " ") chC <- true } }() go func() { defer wg.Done() var letters []byte = []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'} for _, letter := range letters { <-chC fmt.Print(string(letter), " ") chN <- true } }() chN <- true wg.Wait() ``` |
6
wwhontheway 51 天前 ![]() 不能预设 goroutine 的运行时机和顺序
|
![]() |
7
rower 51 天前
我建议先看看正确的答案怎么写的
|
![]() |
8
pkoukk 51 天前 ![]() 用两个 channel
6# 说的很对,不要对 goroutine 的运行时间和顺序有任何预设 如果他们有顺序,那你开 goroutine 是图个啥? |
![]() |
10
bronyakaka 51 天前 ![]() 假设第二个 go 先执行,执行到<-ch 会被阻塞,卡住,因为此时通道没元素;
这个时候第一个 go 就肯定执行了,一旦执行了 ch <- true ,第 2 个 go 就可以完成<-ch 继续执行了,当然也有可能由于比较慢导致第一个 go 继续执行,但此时会被<-ch 阻塞,这种情况下肯定先打印字母; 假设第一个 go 先执行,一路打印了数字走到 ch <- true ,此时第二个 go 执行了,畅通无阻可以走到打印字母的地方。这种情况就是交叉了,先数字再字母 |
![]() |
11
rower 50 天前
我建议谷歌搜下标题,或者问 AI ,这个是常见的问题,有答案的
|