Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sync.Mutex and *sync.Mutex Which is better?

Tags:

go

mutex

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?

like image 219
Anderson Avatar asked Apr 13 '18 02:04

Anderson


3 Answers

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:

  • performance: if the instances being copied are big struct|s, and the calls happen in the hot path of you application, you will be copying significant amounts of data around.
  • memory usage: similarly to the point above: if you have long-running goroutines that receive large struct|s, these copies might have an impact on your app's memory footprint.
  • semantics: this is arguably the most important difference: if your type has methods that should modify its contents, then you pretty much have to use pointer receivers. Otherwise the method would act on a copy and the changes would be invisible. The corollary is equally important: if you want to signal that your methods will not modify their receiver, using a concrete type (probably with unexported contents) is a good way to achieve that.

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(){...}
like image 69
Leo Antunes Avatar answered Oct 28 '22 02:10

Leo Antunes


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

like image 23
Bilal Azizieh Avatar answered Oct 28 '22 02:10

Bilal Azizieh


  • I think you can regard *sync.Mutex as a simple pointer. If you want to use it, you should declare and init it, but if you use sync.Mutex, it has been inited.
  • BTW, in k8s source code, they always pass variable pointer to use, because passing struct will do a copy, but if you use pointer, all you need to pass is a pointer. ( I mean is you don't not need to have a copy spend).
like image 26
Layne Liu Avatar answered Oct 28 '22 02:10

Layne Liu