golang struct concurrent read and write without Lock is also running ok?

Issue

concurrentMap() function Have WARNING: DATA RACE, And fatal error: concurrent map read and map write

concurrentStruct() have WARNING: DATA RACE, But running ok

why the struct can DATA RACE?

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 {}
}

Solution

Unsynchronized, concurrent access to any variable from multiple goroutines where at least one of them is a write is undefined behavior. Do not try to find logic in undefined behavior, just use proper synchronization. Undefined means it may run “correctly” or it may run “incorrectly” (giving incorrect results), or it may crash or anything else. That’s what undefined means. Read more about it here: Is it safe to read a function pointer concurrently without a lock?

Your concurrentStructWithMuLock() has actually no data race, because you are using a mutex to properly synchronize access to the struct.

And with concurrentMap() that’s another issue. Go 1.6 added a lightweight concurrent misuse of maps detection to the runtime:

The runtime has added lightweight, best-effort detection of concurrent misuse of maps. As always, if one goroutine is writing to a map, no other goroutine should be reading or writing the map concurrently. If the runtime detects this condition, it prints a diagnosis and crashes the program. The best way to find out more about the problem is to run the program under the race detector, which will more reliably identify the race and give more detail.

So this is an intentional crash by the runtime, because it detects unsynchronized access to the map. This is a “feature” of the Go runtime, and it crashes your app because no data race should be left in your app (to prevent undefined behavior). Read more about it here: How to recover from concurrent map writes?

Answered By – icza

Answer Checked By – Katrina (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.