
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 212 天前 via Android 用两个 channel |
2 kirara2024 212 天前 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 212 天前 goroutine 的运行不是按顺序的,一般是 FILO ,只有饥饿了才会 FIFO ,你还要考虑下其他 M 空闲时窃取 G |
4 rekulas 212 天前 这才是符合预期的,你用两个协程打印,就算用了 chan 又如何,你不能保证一个协程提交了数据马上又自己抢占到,这又不是一个原子操作,而且在不同硬件,环境的机器上估计结果也有差距.. 要稳定的两个协程打印数字最稳的还是一个加锁,确认打印完成了另一个才拿到锁就行了,或者用协程池的模式顺序执行协程队列 |
5 dallaslu 212 天前 这是在模拟《功夫》中的拍卖过程吗?按套路来,一人一句,不能喊乱了。顺着 @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 212 天前 不能预设 goroutine 的运行时机和顺序 |
7 rower 212 天前 我建议先看看正确的答案怎么写的 |
8 pkoukk 212 天前 用两个 channel 6# 说的很对,不要对 goroutine 的运行时间和顺序有任何预设 如果他们有顺序,那你开 goroutine 是图个啥? |
假设第二个 go 先执行,执行到<-ch 会被阻塞,卡住,因为此时通道没元素; 这个时候第一个 go 就肯定执行了,一旦执行了 ch <- true ,第 2 个 go 就可以完成<-ch 继续执行了,当然也有可能由于比较慢导致第一个 go 继续执行,但此时会被<-ch 阻塞,这种情况下肯定先打印字母; 假设第一个 go 先执行,一路打印了数字走到 ch <- true ,此时第二个 go 执行了,畅通无阻可以走到打印字母的地方。这种情况就是交叉了,先数字再字母 |
11 rower 212 天前 我建议谷歌搜下标题,或者问 AI ,这个是常见的问题,有答案的 |