V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
ldimple
V2EX  ›  Go 编程语言

go 语言大佬请进,三协程按序打印 abc 到底哪里出错

  •  
  •   ldimple · 2021-03-05 12:36:59 +08:00 · 2629 次点击
    这是一个创建于 1366 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这段代码的问题在于大部分情况是 ABC 按序输出的,但有时候 A 协程没有打印 A,结果变成了 BC

    type cond struct{ sema1 int32 sema2 int32 } func (c *cond)printA(){ for { if c.sema1==0{ println("A") atomic.CompareAndSwapInt32(&c.sema1,0,1) } }

    }

    func (c *cond)printB(){ for { if c.sema1==1&& c.sema2==0{ println("B") atomic.CompareAndSwapInt32(&c.sema2,0,1) } } }

    func (c *cond) printC(){ for { if c.sema2==1 { println("C") println("--------------------------------") atomic.CompareAndSwapInt32(&c.sema1, 1, 0) atomic.CompareAndSwapInt32(&c.sema2, 1, 0) } } }

    func main() { var con =new (cond) con.sema1=0 con.sema2=0 go con.printC() go con.printB() go con.printA() time.Sleep(5*time.Second)

    }

    16 条回复    2021-03-06 16:04:50 +08:00
    ldimple
        1
    ldimple  
    OP
       2021-03-05 12:39:01 +08:00
    type cond struct{
    sema1 int32
    sema2 int32
    }
    func (c *cond)printA(){
    for {
    if c.sema1==0{
    println("A")
    atomic.CompareAndSwapInt32(&c.sema1,0,1)
    }
    }

    }

    func (c *cond)printB(){
    for {
    if c.sema1==1&& c.sema2==0{
    println("B")
    atomic.CompareAndSwapInt32(&c.sema2,0,1)
    }
    }
    }


    func (c *cond) printC(){
    for {
    if c.sema2==1 {
    println("C")
    println("--------------------------------")
    atomic.CompareAndSwapInt32(&c.sema1, 1, 0)
    atomic.CompareAndSwapInt32(&c.sema2, 1, 0)
    }
    }
    }

    func main() {
    var con =new (cond)
    con.sema1=0
    con.sema2=0
    go con.printC()
    go con.printB()
    go con.printA()
    time.Sleep(5*time.Second)

    }
    LoNeFong
        2
    LoNeFong  
       2021-03-05 12:58:50 +08:00
    友情提示, v 站支持 Markdown
    sealingpp
        3
    sealingpp  
       2021-03-05 13:02:54 +08:00
    ldimple
        4
    ldimple  
    OP
       2021-03-05 13:05:57 +08:00 via Android
    @sealingpp 谢谢,我也会用管道,只是想试试这种无锁变量的方法
    ldimple
        5
    ldimple  
    OP
       2021-03-05 13:07:34 +08:00 via Android
    @LoNeFong 第一次发帖哈哈
    Shakeitin
        6
    Shakeitin  
       2021-03-05 13:20:17 +08:00
    printC() 执行了 2 次 atomic 操作,但这两次操作的整体过程显然并不是原子的

    ```go
    println("C") // printC()
    println("--------------------------------") // printC()
    atomic.CompareAndSwapInt32(&c.sema1, 1, 0) // printC()
    println("B") // printB()
    atomic.CompareAndSwapInt32(&c.sema2, 1, 0) // printC()
    atomic.CompareAndSwapInt32(&c.sema2, 0, 1) // printB()
    println("C") // printC(), 下一次循环
    println("--------------------------------") // printC()
    ```
    Leprax
        7
    Leprax  
       2021-03-05 13:25:03 +08:00
    ```go
    type cond struct{ sema1 int32 sema2 int32 } func (c *cond)printA(){ for { if c.sema1==0{ println("A") atomic.CompareAndSwapInt32(&c.sema1,0,1) } }

    }

    func (c *cond)printB(){ for { if c.sema1==1&& c.sema2==0{ println("B") atomic.CompareAndSwapInt32(&c.sema2,0,1) } } }

    func (c *cond) printC(){ for { if c.sema2==1 { println("C") println("--------------------------------") atomic.CompareAndSwapInt32(&c.sema1, 1, 0) atomic.CompareAndSwapInt32(&c.sema2, 1, 0) } } }

    func main() { var con =new (cond) con.sema1=0 con.sema2=0 go con.printC() go con.printB() go con.printA() time.Sleep(5*time.Second)

    }
    ```
    ldimple
        8
    ldimple  
    OP
       2021-03-05 14:02:46 +08:00
    @Shakeitin 您是对的,已解决
    mauve
        9
    mauve  
       2021-03-05 15:30:27 +08:00
    我运行了好多遍楼主的代码,结果都是

    A
    B
    C
    --------------------------------
    A
    B
    C
    --------------------------------
    A
    B
    C
    --------------------------------
    A
    B
    C
    --------------------------------

    这样输出,没有出现其他情况,楼主你说「但有时候 A 协程没有打印 A,结果变成了 BC 」

    是什么样子的输出结果,可以解释一下吗
    writesome6
        10
    writesome6  
       2021-03-05 16:39:09 +08:00
    用一个信号量就行了

    ```
    const (
    printA = iota
    printB
    printC
    )

    type cond struct{
    sema int32
    }
    func (c *cond)printA(){
    for {
    if c.sema == printA{
    println("A")
    swapInt32 := atomic.CompareAndSwapInt32(&c.sema, 0, printB)
    if !swapInt32 {
    panic("A")
    }
    }
    }

    }

    func (c *cond)printB(){
    for {
    if c.sema == printB{
    println("B")
    if !atomic.CompareAndSwapInt32(&c.sema,printB,printC) {
    panic("B")
    }
    }
    }
    }


    func (c *cond) printC(){
    for {
    if c.sema==printC {
    println("C")
    println("--------------------------------")
    if !atomic.CompareAndSwapInt32(&c.sema, printC, printA) {
    panic("C")
    }
    }
    }
    }

    func main() {
    var con =new (cond)
    go con.printC()
    go con.printB()
    go con.printA()
    time.Sleep(5*time.Second)

    }
    ```
    Glauben
        11
    Glauben  
       2021-03-05 16:52:10 +08:00
    看的好难受啊
    katsusan
        12
    katsusan  
       2021-03-05 19:30:11 +08:00
    @ldimple 按照楼上所说 printC 里即使两次不是原子操作,由于先执行的 CAS(sem1, 1, 0),此时 sema1=0, sema2=1,
    应该也不会触发 printB 的条件(sema1=1,sema2=0)吧,为何会走进下面的 printB 逻辑里。
    ldimple
        13
    ldimple  
    OP
       2021-03-06 12:43:33 +08:00   ❤️ 1
    @katsusan cpu 有可能乱序执行,所以要加锁确保执行顺序如代码所示
    ldimple
        14
    ldimple  
    OP
       2021-03-06 12:45:43 +08:00
    @mauve 就是出现 A 没有打印的情况,比较少,但是有,建议这题采取 channel 的方式来做,上面有人给了很好的示例
    Shakeitin
        15
    Shakeitin  
       2021-03-06 15:44:24 +08:00
    @katsusan #12 虽然锅基本会落在乱序执行上,但我给的那个答案的确不对。。
    Shakeitin
        16
    Shakeitin  
       2021-03-06 16:04:50 +08:00
    @katsusan #12
    @ldimple #14

    应该是 printB() 的判断的两个部分分别落在了 pinrtC() 的两个 cas 的前后
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2805 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 05:49 · PVG 13:49 · LAX 21:49 · JFK 00:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.