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

2021-03-05 12:36:59 +08:00
 ldimple

这段代码的问题在于大部分情况是 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)

}

2611 次点击
所在节点    Go 编程语言
16 条回复
ldimple
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
2021-03-05 12:58:50 +08:00
友情提示, v 站支持 Markdown
sealingpp
2021-03-05 13:02:54 +08:00
ldimple
2021-03-05 13:05:57 +08:00
@sealingpp 谢谢,我也会用管道,只是想试试这种无锁变量的方法
ldimple
2021-03-05 13:07:34 +08:00
@LoNeFong 第一次发帖哈哈
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
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
2021-03-05 14:02:46 +08:00
@Shakeitin 您是对的,已解决
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
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
2021-03-05 16:52:10 +08:00
看的好难受啊
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
2021-03-06 12:43:33 +08:00
@katsusan cpu 有可能乱序执行,所以要加锁确保执行顺序如代码所示
ldimple
2021-03-06 12:45:43 +08:00
@mauve 就是出现 A 没有打印的情况,比较少,但是有,建议这题采取 channel 的方式来做,上面有人给了很好的示例
Shakeitin
2021-03-06 15:44:24 +08:00
@katsusan #12 虽然锅基本会落在乱序执行上,但我给的那个答案的确不对。。
Shakeitin
2021-03-06 16:04:50 +08:00
@katsusan #12
@ldimple #14

应该是 printB() 的判断的两个部分分别落在了 pinrtC() 的两个 cas 的前后

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/758752

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX