Randix Lai Randy
Randix Lai Randy

Reputation: 83

Race condition when printing struct field even with mutex

Why is the mutex.Lock() not working in the code below? Every time I run race test it shows there are races.

I tried to use the lock within the child struct, then parent struct to lock the state, but none of them worked. The race test keeps saying there is race condition.

I've tried both t.Lock() and t.data.Lock().

type Test struct {
    name string
    data Data
    sync.RWMutex //Should I put it here?
}

type Data struct {
    d map[string]int
    sync.RWMutex // Should I put it here?
}

func (t *Test) add(key string) {
    t.data.Lock()
    defer t.data.Unlock()
    t.data.d[key] += 1
}

func (t *Test) read() {
    for {
        t.data.Lock()
        _= t.data.d["test"]
        t.data.Unlock()
    }
}

func main() {
    t := &Test{}
    t.name = "oops"
    t.data = Data{}
    t.data.d = make(map[string]int)
    t.data.d["test"] = 1

    for i := 0; i <= 10; i++ {
        go func(t *Test) {
            t.add("test")
        }(t)
        go func(t *Test) {
            t.read()
        }(t)
    }
    time.Sleep(time.Second * 3)
    fmt.Printf("result is %v", t.data.d["test"])

Upvotes: 2

Views: 678

Answers (2)

Randix Lai Randy
Randix Lai Randy

Reputation: 83

As @peterSO mentioned, the error is caused by "fmt.Printf("result is %v\n", t.data.d["test"])". After hours digging, I seem to have found the answers about my first two questions. I misunderstood the concept of mutex. A mutex is used to protect the resource and not to lock the memory it self , in my example, the struct itself. For the first question: if one goroutine executes some code like

s.pingLock.Lock()
\\ some logic 1
s.pingLock.Unlock()
streamLock
\\ some logic 2
streamUnlock

So when one goroutine executes this code and acquires the s.streamLock.Lock(), until it's unlocked, other go routines cannot execute some logic 2, but any goroutine can execute some logic1 if it gets the pingLock.Lock(). If using one lock only , if one go routine gets the lock, no one else can get the lock then all other executions get blocked.

For the second question: If understanding the above explanation. Then the second question is answered too. Put mutex anywhere you want because it's just a lock to protect the resource, that is to say, the code itself. I'm sure there's a nice idiomatic way to do it, though.

I'm not sure if it's right to take it this way. If anyone has a different opinion or better answer , please let me know.

Upvotes: 1

peterSO
peterSO

Reputation: 166616

The race test keeps saying there is race condition.


The statement

fmt.Printf("result is %v", t.data.d["test"])

passes the argument

t.data.d["test"]

by value. It makes a copy by assignment, which is a read.

You need to protect the read via the mutex.

t.data.Lock()
fmt.Printf("result is %v\n", t.data.d["test"])
t.data.Unlock()

Upvotes: 1

Related Questions