In Go, we can use:
type Data struct {
lock *sync.Mutex
}
or
type Data struct {
lock sync.Mutex
}
And, use like this:
func (d *Data) Update() {
d.lock.Lock()
defer d.lock.Unlock()
// update
}
The difference I can think of is that *sync.Mutex
needs instantiation to use only.
What is the difference between sync.Mutex
and *sync.Mutex
in Go and which one is better?
The comment from mkopriva is correct and should probably be the accepted answer.
However, reading OP's question, I think there might be one potential misunderstanding worth expanding on: OP's mention that the only difference is "one has to be initialized, the other doesn't".
The fundamental difference before a pointer to T
and T
implies a series of behavior changes when using the variable, only one of which being its instantiation.
Firstly: the actual instantiation exists in both cases. In the T
case it's implicit in the declaration, because the variable contains the instance itself (so to speak). But in the pointer case, it may happen at a totally different place in the code, since the variable contains only an indirection to the instance. This explains, among other things, why only the pointer variant can lead to "nil pointer dereference": only in this case can your code attempt to do anything with the variable before it was actually initialized.
Secondly: using a concrete T
together with the fact that go is a "pass by value" language, means any concrete function arguments (or method receivers) are copied for each call.
This has consequences in at least three areas:
struct
|s, and the calls happen in the hot path of you application, you will be copying significant amounts of data around.struct
|s, these copies might have an impact on your app's memory footprint.Finally, this leads us to the concrete case of sync.Mutex
: looking at the points above and the code, we can see that performance and memory usage are usually not an issue, because sync.Mutex
is a pretty small struct
.
However, the last point is pretty important: what does it mean to have a pointer to a sync.Mutex
? It means a copy of the containing struct
will point to the same lock. I.e.: it means two instances of your struct
might share a lock.
Since go vet
will not complain about copying pointers to mutexes, copying your parent struct
will raise no alarm bells and you might end up protecting two separate instances with the same lock, potentially leading to deadlocks.
In summary: unless you know you want to protect different copies of something with the same lock (IMHO somewhat unlikely), you're better off using concrete sync.Mutex
|es.
If the only reason for making a sync.Mutex
pointer is because go vet
told you not to copy it, then you should probably consider looking one layer up at the struct
you are trying to protect: most likely you're copying it unintentionally by having a concrete receiver like
func (t T) foo(){...}
where you should have
func (t *T) foo(){...}
They are not exactly the same:
sync.Mutex: here you can just use the function Lock
of Unlock
immediately.
*sync.Mutex: here you have to initialize before using the function like if you want to copy the instance from other objects, because if you try to use the above way (without pointer) to copy from other object it'll show you that warning assignment copies lock value to xxxxxx: sync.Mutex copylocks
witch you can find the solution of it in this answer.
but both of the types accepts you to use the function Lock
or Unlock
because the function are declared as Pointer receivers and this way of declaring a function accepts to use directly on an object or on a pointer like what you have
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With