When do you embed mutex in struct in Go?

2019-01-27 06:07发布

问题:

NOTE: I found the word 'embed' in the title was bad choice, but I will keep it.

I see a lot of code does like this:

type A struct {
    mu sync.Mutex
    ...
}

And use it like this:

a := &A{}

a.mu.Lock()
defer a.mu.Unlock()

a.Something()

Is it better than local mutex or global mutex?

a := &A{}

var mu sync.Mutex
mu.Lock()
defer mu.Unlock()

a.Something()

When should I use former, or later?

回答1:

It's good practice to keep the mutex close to the data it is destined to protect. If a mutex ought to protect concurrent access to fields of a struct value, it's very convenient to add the mutex as a field of that struct, so its purpose is obvious.

If in your app there is only a single "instance" of A, it's fine to make the mutex a global variable too.

If your app is to create multiple values of A, all of which needs to be protected from concurrent access (but only individually, multiple values may be accessed concurrently), then obviously a global mutex is a bad choice, it would limit the concurrent access to a single value of A in any point in time.

Adding the mutex to the struct as a field, you will naturally have a separate mutex for each distinct struct values, responsible to guard that single, containing struct value (or its fields).

Although adding a mutex as in your example is not embedding, it's a regular, named field. An embedded field declaration omits the field name.

It's known and used to a lesser extent, but it's also handy that you can "truly" embed a mutex in a struct, and you can call Lock() and Unlock() as if they would be part of the struct itself. It looks like this:

var hits struct {
    sync.Mutex
    n int
}

hits.Lock()
hits.n++
hits.Unlock()

(This example is taken from 10 things you (probably) don't know about Go, slide #3.)