How to apply separate Mutex on multiple variables in Golang?
Asked Answered
J

3

5

I have multiple variables which I want to make mutually exclusive using this method

type var1WithMutex struct {
    mu       sync.Mutex
    var1     int
}
func (v *var1) Set(value int) {
    v.mu.Lock()
    v.var1 = value
    v.mu.Unlock()
}
func (v *var1) Get() (value int) {
    v.mu.Lock()
    value = v.var1
    v.mu.Unlock()
    return
}

Similarly there are hundreds of variable, like var1, var2, var3.... var100
How do i make all of them mutually exclusive without repeating this code?
Note that var1, var2, var3 etc are not part of an array and no way related to each other. var2 may be a int and var3 may be User{}

Jessi answered 19/11, 2014 at 15:34 Comment(4)
Do you really need fine-grained locking? You could wrap them all into on or more containers of some sort that lock them as groups. Otherwise if you want locks for every variable, you need to write locks for every variable. You could also look into code generation and go gen.Equipollent
I am confused: Each and every variable of type var1 will be protected by its own mutex. var1 is not a variable but a type. And what is the point of locking the read in Get? Why do you have hundreds of types which need protection by a mutex? Just don't do that.Ixion
@Ixion there were two var1 first was a "type" and another was a int variable inside Struct. I was talking about the int var1. Now I have updated the Type name to var1WithMutex. Now its a bit clear. By the way I was looking for approach followed by ogc-nick Its very fundamental but it did not came to me at that instantJessi
@Equipollent I dont need it now, but what if I am in a situation when I need that. I can not wrap them all inside one mutex because then it will make all the subroutines wait when one of the variable is being used. That will unnecessarily make it slow. Thanks for suggesting go gen I will look into its usage.Jessi
D
3

You could make different Mutex object for each type instead. Playground

type MutexInt struct {
    sync.Mutex
    v int
}

func (i *MutexInt) Get() int {
    return i.v
}

func (i *MutexInt) Set(v int) {
    i.v = v
}

and use it like this

func main() {
    i := MutexInt{v: 0}
    i.Lock()
    i.Set(2)
    fmt.Println(i.Get())
    i.Unlock()
}
Disinherit answered 19/11, 2014 at 16:44 Comment(6)
This is no different than the original code, other than you now need to explicitly lock and unlock. Also, you don't need to re-initalze the zero value of sync.Mutex, just make i := MutexInt{v: 0}Equipollent
Thanks, I was looking for similar approach. I have edited the answer, it will be visible after peer review.Jessi
@Equipollent Shouldn't the user of a Mutex object be locking/unlocking it? Why would the locks go inside the get/set methods?Disinherit
@ogc-nick: because it's less error prone that way, and you only have to type the locking code once. The canonical locked Get method calls, i.Lock(); defer i.Ulnock(); return i.Val.Equipollent
@Equipollent How is that better than just returning i.Val?Disinherit
@ogc-nick: it ensures that reads are all correctly locked on a concurrent data structure.Equipollent
D
2

You can wrap your variables and use a shared mutex, if they all have the same interface (http://play.golang.org/p/xri2M-rtEY):

type Var interface {
    Get() int
    Set(n int)
}

func Sync(v Var, m *sync.RWMutex) Var {
    return &syncedVar{
        v: v,
        m: m,
    }
}

type syncedVar struct {
    m *sync.RWMutex
    v Var
}

func (v syncedVar) Get() int {
    v.m.RLock()
    defer v.m.RUnlock()
    return v.v.Get()
}

func (v *syncedVar) Set(n int) {
    v.m.Lock()
    defer v.m.Unlock()
    v.v.Set(n)
}
Dramamine answered 19/11, 2014 at 19:4 Comment(0)
R
2

If your variables are primitive data types (int, float,..), use the sync.atomic package. Atomic operations do not need mutex.

Rattletrap answered 15/3, 2017 at 8:6 Comment(1)
Good suggestion, but only applies to ints, not to floats.Hog

© 2022 - 2024 — McMap. All rights reserved.