带互斥锁的竞争条件以及在何处嵌入锁,父结构或子结构?

  1. I see some people out some mutexes within a single struct. Is it necessary? Or can we simply put one single lock there? example:
type Session struct {
    // some other irrelevant code
    pingLock sync.Mutex
    // some other irrelevant code
    streamLock sync.Mutex
    // some other irrelevant code
    shutdownLock sync.Mutex
}
  1. If a struct contains another struct, and the child struct contains a map or a slice, where should I put the mutex, the parent struct or the child struct?
  2. Why is the mutex.Lock() not working in my code? Every time I run race test it shows there are races.

At first I was trying to solve the first two problems so I wrote some demo code to test it out. 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"])

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
", t.data.d["test"])
t.data.Unlock()

As @peterSO mentioned, the error is caused by "fmt.Printf("result is %v ", 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.