关于 GO 语言字母与数字交叉打印的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Betsy
V2EX    Go 编程语言

关于 GO 语言字母与数字交叉打印的问题

 
  •   Betsy 213 天前 2449 次点击
    这是一个创建于 213 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 

    如果我想稳定的先打印数字,再打印字母,应该如何修改?

    第 1 条附言    212 天前

    感谢大家,改成下述写法就正确了。

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

    假设第一个 go 先执行,一路打印了数字走到 ch <- true ,此时第二个 go 执行了,畅通无阻可以走到打印字母的地方。这种情况就是交叉了,先数字再字母
    rower
        11
    rower  
       212 天前
    我建议谷歌搜下标题,或者问 AI ,这个是常见的问题,有答案的
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     900 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 20:08 PVG 04:08 LAX 12:08 JFK 15:08
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86