golang struct在没有锁定的情况下并发读写也可以运行吗?

时间:2018-07-12 08:58:30

标签: dictionary go struct goroutine concurrenthashmap

concurrentMap()函数具有WARNING: DATA RACE致命错误concurrent map read and map write

concurrentStruct()有警告:数据竞赛,但运行正常

为什么该结构可以进行数据竞赛?

package main

import (
    "sync"
)

func main() {
    // concurrentMap()
    concurrentStruct()
    // concurrentStructWithMuLock()
}

type Metadata struct {
    mu  sync.RWMutex // 
    key bool
}

// concurrentStruct 并发操作结构体
// concurrent read and write the struct
// go run -race  main.go   有 WARNING: DATA RACE,但是可以运行
// go run -race  main.go   It have WARNING: DATA RACE, But running ok
func concurrentStruct() {
    m := new(Metadata)

    for i := 0; i < 100000; i++ {
        go func(metadata *Metadata) {
            for {
                readValue := metadata.key
                if readValue {
                    metadata.key = false
                }
            }
        }(m)

        go func(metadata *Metadata) {
            for {
                metadata.key = true
            }
        }(m)
    }

    select {}
}

// concurrentStructWithMuLock  并发操作(使用了读写锁)结构体
// concurrent read and write the struct with RWMutex
// go run -race  main.go   没有 WARNING: DATA RACE
// go run -race  main.go   Don't have WARNING: DATA RACE, and running ok
func concurrentStructWithMuLock() {
    m := new(Metadata)

    go func(metadata *Metadata) {
        for {
            metadata.mu.Lock()
            readValue := metadata.key
            if readValue {
                metadata.key = false
            }
            metadata.mu.Unlock()
        }
    }(m)

    go func(metadata *Metadata) {
        for {
            metadata.mu.Lock()
            metadata.key = true
            metadata.mu.Unlock()
        }
    }(m)

    select {}
}

// concurrentMap 并发读写 Map
// concurrent read and write the map
// go run -race  main.go   有 WARNING: DATA RACE,不可运行,fatal error: concurrent map read and map write
// go run -race  main.go  Have WARNING: DATA RACE, And fatal error: concurrent map read and map write
func concurrentMap() {
    m := make(map[int]int)
    go func() {
        for {
            _ = m[1]
        }
    }()
    go func() {
        for {
            m[2] = 2
        }
    }()
    select {}
}

1 个答案:

答案 0 :(得分:4)

从多个goroutine中至少其中之一是写操作的对任何变量的非同步并发访问是未定义行为。不要尝试以未定义的行为查找逻辑,只需使用适当的同步即可。未定义意味着它可能会“正确”运行,也可能“不正确”运行(给出错误的结果),或者崩溃或发生其他任何情况。那就是未定义的意思。在此处详细了解:Is it safe to read a function pointer concurrently without a lock?

您的concurrentStructWithMuLock()实际上没有数据争用,因为您正在使用互斥锁正确同步对结构的访问。

而使用concurrentMap()则是另一个问题。 Go 1.6 added对地图检测到运行时的轻度并发滥用:

  

运行时已添加了轻巧的尽力检测并发滥用地图的功能。与往常一样,如果一个goroutine正在写入地图,则其他goroutine不应同时读取或写入地图。 如果运行时检测到这种情况,它将打印诊断信息并使程序崩溃。找出更多有关该问题的最佳方法是在race detector下运行该程序,这将更加可靠确定种族并提供更多细节。

因此这是运行时故意崩溃的原因,因为它检测到对地图的非同步访问。这是Go运行时的“功能”,它会使您的应用程序崩溃,因为您的应用程序中不应保留任何数据竞争(以防止发生未定义的行为)。在此处详细了解:How to recover from concurrent map writes?