18

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?

Anderson
  • 240
  • 1
  • 2
  • 11
  • 19
    Same rules apply as to any other `*T` vs `T`. With `*T` you can share an instance of `T`, without `*` you get a new copy of `T` each time you pass it around. Use a pointer if you need to share a lock, don't if you don't. With respect to how the `sync.Mutex` type is commonly used [see here](https://github.com/golang/go/search?p=1&q=%22sync.Mutex%22&type=&utf8=%E2%9C%93). – mkopriva Apr 13 '18 at 03:53
  • @mkopriva That looks like an answer. Perhaps it should be made so. – Michael Hampton Apr 13 '18 at 03:56
  • @mkopriva if you use *sync.Mutex you get a new copy of uintptr(uint64), if you use sync.Mutex you get struct{int 32,int32} ,i think its the same. – Anderson Apr 13 '18 at 04:03
  • 2
    The answer to which is better depends on the context where the code is used. If it's a function parameter, then the first option is the only one that's correct. If it's a variable declaration, then the second is more convenient. Please show the surrounding code. – Charlie Tumahai Apr 13 '18 at 04:04
  • @Cerise Limón its different question. my question is has another difference? like use sync.Mutex is unsafe in some time? – Anderson Apr 13 '18 at 05:44
  • 1
    @Anderson what makes you think `uintptr(uint64)` and `struct{int32,int32}` are the same? – mkopriva Apr 13 '18 at 08:20
  • @Anderson your `Data.Update` example will work just the same with a pointer lock as with a non-pointer lock. That said, in the given example I would use a *non-pointer* though. **A**.) It doesn't seem like you're going to be passing lock around too much. **B**.) Using a non-pointer field means that the lock is gonna be auto-allocated when you allocate the `Data` value, i.e. less code, **C**.) You can be certain that `Lock/Unlock` is not gonna be called on a `nil` pointer by accident. – mkopriva Apr 13 '18 at 08:32
  • @Anderson also keep in mind that you should not copy a `Data` instance, if you have multiple consumers that need to use the same `Data`, pass a pointer to it rather than a value. From the [`sync` package docs](https://golang.org/pkg/sync/): *"Values containing the types defined in this package should not be copied. "* – mkopriva Apr 13 '18 at 08:38
  • 1
    my conclusion:if lock as a parameter of a function, it should be pointer. – Anderson Apr 13 '18 at 08:54

3 Answers3

13

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(){...}
Leo Antunes
  • 689
  • 7
  • 11
4
  • 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).
Layne Liu
  • 452
  • 5
  • 10
  • so *sync.Mutex is always better then sync.Mutex? – Anderson Apr 13 '18 at 05:48
  • @Anderson nope. – zerkms Apr 13 '18 at 05:51
  • 4
    "If the parameter is too large, the later will be faster." --- this is really the terrible conclusion. Size is not the only thing to consider. Pointer values are candidates to be allocated on the heap, which means GC pressure, which is not free. So one can never state pointers are better than raw values without profiling/benchmarking. – zerkms Apr 13 '18 at 05:52
  • @zerkms another question: sync.Mutex always not be allocated on heap? – Anderson Apr 13 '18 at 05:58
  • sorry, I say it so absolutism(0.0). I only want to express i think it's a good practice in programing. – Layne Liu Apr 13 '18 at 06:38
  • @Anderson the spec does not have a notion of the heap and stack. So it's code-, go compiler- and go runtime- specific. – zerkms Apr 13 '18 at 06:59
  • 6
    "i think it's a good practice in programing" --- unless it's not: pointer's semantic is about shared ownership, it's not a performance optimisation tool. Using a pointer just because "I like it" or "somebody recommended it" makes no sense. – zerkms Apr 13 '18 at 07:00
  • @zerkms "Pointer values are candidates to be allocated on the heap" ,but Value are candidates to be allocated on the heap, so its no difference – Anderson Apr 13 '18 at 07:16
  • @zerkms Profound Thought. – Layne Liu Apr 13 '18 at 07:22
  • @Anderson I found something maybe helpful. https://golang.org/doc/faq#stack_or_heap – Layne Liu Apr 13 '18 at 07:27
  • @MauriceAiken we dont need to know a variable is allocated on the heap or the stack , how to know the different between *sync.Mutex and sync.Mutex? in my mind, sync.Mutex is the same safe as *sync.Mutex. – Anderson Apr 13 '18 at 07:39
  • @Anderson as I mentioned above - pointers are about shared ownership. It's tangential to where it was allocated. – zerkms Apr 13 '18 at 07:48
  • @zerkms so if its a private variable ,only be used in private function. like Update above . should use sync.Mutex. if its a public variable , use *sync.Mutex? – Anderson Apr 13 '18 at 07:55
  • @Anderson you use pointers when you need to share the same value among multiple consumers. You should not use pointers unless you can prove you need to use them – zerkms Apr 13 '18 at 07:56
  • sync.Mutex is safe when concurrence? – Anderson Apr 13 '18 at 07:59
  • 2
    @Anderson pointer or non-pointer has no relevance as to the safety of `sync.Mutex`'s concurrent use. Private or public variable has no relevance as to the use of pointers vs non-pointers. Your `Update` method will work the exact same way, whether you use pointer or not. – mkopriva Apr 13 '18 at 08:18
0

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