golang 中并发读写同一个变量会出现部分错乱吗?

2018-11-04 15:49:22 +08:00
 helloworld12

譬如一个 int64 两个协程并发修改

那么会出现一个协程 A 修改了前面 32 位,
然后切换到另一个协程 B 把 64 位都修改了,
接着协程 A 继续修改后 32 位....
最终的结果是 B 决定了前面 32 位,A 决定了后面 32 位...数值上既不是 A 的结果,也不是 B 的结果

嗯, golang 里面的协程是自愿让渡的模式, 但是协程依赖的线程,依旧是操作系统的抢占是模式

也就是说有可能出现,一个协程修改某个值,修改到一半,被挂起, 所以是可能的,这样推论是否正确

int64 位应该没可能, 因为汇编里面有对应的 int64 类型的指令, 不过,如果是个 struct 结构...应该有可能出现这个问题

谢谢

4259 次点击
所在节点    问与答
6 条回复
innoink
2018-11-04 15:57:40 +08:00
x64 是不会这样的
实际上一个 cacheline 的都是原子
bigpigeon
2018-11-04 17:26:59 +08:00
写个单元测试就知道了
mx1700
2018-11-04 17:42:40 +08:00
这时候就需要锁了
mritd
2018-11-05 10:04:45 +08:00
Atomic
reus
2018-12-03 09:24:07 +08:00
要加锁。
goodwong
2019-05-29 11:42:57 +08:00
struct 会有差异,见代码:
```go

func structRaceWrong() {

x := struct {
A int
B int
}{1, 1}

var wg sync.WaitGroup

wg.Add(1)
go func() {
for i := 0; i < 1000000; i++ {
if x.A != x.B {
log.Println("A != B", x.A, x.B)
}
if i < 3 {
log.Printf("%p\n", &x)
}
}
wg.Done()
}()

wg.Add(1)
go func() {
for i := 0; i < 1000000; i++ {
x = struct {
A int
B int
}{x.A + 1, x.B + 1} // <---------- 导致 A、B 可能不一致
}
wg.Done()
}()

wg.Wait()
}

// 结论:Go 是值拷贝,结构体会出现复制一半就被其它读取了
func structRaceWrong2() {

x := struct {
A int
B int
}{1, 1}

var wg sync.WaitGroup

wg.Add(1)
go func() {
for i := 0; i < 1000000; i++ {
cp := x
if cp.A != cp.B {
log.Println("A != B", cp.A, cp.B)
}
if i < 3 {
log.Printf("%p\n", &x)
}
}
wg.Done()
}()

wg.Add(1)
go func() {
for i := 0; i < 1000000; i++ {
cp := x // <---------- 拷贝也不行,可能只拷贝一半
x = struct {
A int
B int
}{cp.A + 1, cp.B + 1}
}
wg.Done()
}()

wg.Wait()
}

// 结论:通过指针读取的数据是完整的,但不一定是最新的。
func structRaceOk() {

x := &struct {
A int
B int
}{0, 0}

var wg sync.WaitGroup

wg.Add(1)
go func() {
for i := 0; i < 1000000; i++ {
cp := x
if cp.A != cp.B { // <---------- 通过指针读取的数据是完整的,但不一定是最新的。
log.Fatal("A != B", cp.A, cp.B) // 不会出现
}
if i < 10 { // 抽取前 10 条数据检查
log.Printf("%p -> %p\n", &x, x)
log.Printf("-A%d - i%d = %d", cp.A, i, cp.A - i) // 不一定是最新的
}
if i - cp.A > 1 {
log.Printf("A:%d - i:%d = %d \n", cp.A, i, cp.A - i)
}
}
wg.Done()
}()

wg.Add(1)
go func() {
for i := 0; i < 1000000; i++ {
cp := x
x = &struct {
A int
B int
}{cp.A + 1, cp.B + 1}
}
wg.Done()
}()

wg.Wait()
}

```

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

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

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

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

© 2021 V2EX