V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
helloworld12
V2EX  ›  问与答

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

  •  
  •   helloworld12 · 2018-11-04 15:49:22 +08:00 · 4261 次点击
    这是一个创建于 2210 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

    谢谢

    6 条回复    2019-05-29 11:42:57 +08:00
    innoink
        1
    innoink  
       2018-11-04 15:57:40 +08:00 via Android
    x64 是不会这样的
    实际上一个 cacheline 的都是原子
    bigpigeon
        2
    bigpigeon  
       2018-11-04 17:26:59 +08:00
    写个单元测试就知道了
    mx1700
        3
    mx1700  
       2018-11-04 17:42:40 +08:00 via Android
    这时候就需要锁了
    mritd
        4
    mritd  
       2018-11-05 10:04:45 +08:00 via iPhone
    Atomic
    reus
        5
    reus  
       2018-12-03 09:24:07 +08:00
    要加锁。
    goodwong
        6
    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()
    }

    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2685 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 15:05 · PVG 23:05 · LAX 07:05 · JFK 10:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.