Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a mutex within a struct in Go

I see in Essential Go that using a mutex within a struct is not too straight-forward. To quote from the Mutex Gotchas page:

Don’t copy mutexes

A copy of sync.Mutex variable starts with the same state as original mutex but it is not the same mutex.

It’s almost always a mistake to copy a sync.Mutex e.g. by passing it to another function or embedding it in a struct and making a copy of that struct.

If you want to share a mutex variable, pass it as a pointer *sync.Mutex.

I'm not quite sure I fully understand exactly what's written. I looked here but still wasn't totally clear.

Taking the Essential Go example of a Set, should I be using the mutex like this:

type StringSet struct {
    m map[string]struct{}
    mu            sync.RWMutex
}

or like this?

type StringSet struct {
    m map[string]struct{}
    mu            *sync.RWMutex
}

I tried both with the Delete() function within the example and they both work in the Playground.

// Delete removes a string from the set
func (s *StringSet) Delete(str string) {
    s.mu.Lock()
    defer s.mu.Unlock()
    delete(s.m, str)
}

There will obviously be several instances of a 'Set', and hence each instance should have its own mutex. In such a case, is it preferable to use the mutex or a pointer to the mutex?


1 Answers

Use the first method (a plain Mutex, not a pointer to a mutex), and pass around a *StringSet (pointer to your struct), not a plain StringSet.

In the code you shared in your playground (that version) :

  • .Add(), .Exists() and .Strings() should acquire the lock,
  • otherwise your code fits a regular use of structs and mutexes in go.

The "Don't copy mutexes" gotcha would apply if you manipulated plain StringSet structs :

var setA StringSet
setA.Add("foo")
setA.Add("bar")

func buggyFunction(s StringSet) {
  ...
}


// the gotcha would occur here :
var setB = setA
// or here :
buggyFunction(setA)

In both cases above : you would create a copy of the complete struct

so setB, for example, would manipulate the same underlying map[string]struct{} mapping as setA, but the mutex would not be shared : calling setA.m.Lock() wouldn't prevent modifying the mapping from setB.

like image 99
LeGEC Avatar answered Dec 08 '25 09:12

LeGEC



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!