I have a struct as follows.
type User struct {
Mutex sync.RWMutex
Username string
Stuff map[string]string
}
I also have the following globally
var MyUsers []User
var UserMutex sync.RWMutex
Since my application is accessing the MyUsers slice thousands of times per second, I thought this would be the best way to manage performance. If i want to modify a seperate MyUsers element, then i lock that specific element with:
MyUsers[x].Mutex.Lock()
Then perform any modifications and unlock it.
My question is, Is this a safe way of doing this? I have the normal global UserMutex to lock the slice only when I am appending to the slice... But when appending to a slice, are all of the existing elements moved in memory?
What happens if 1 thread appends to the slice, at the same time whilst another thread is modifying an element? Can this happen in the setup above?
Could i potentially, allocate a big enough slice using make() so that any append()'s wont effect the place in memory?
As you already noticed, it is not safe cause append
might need to allocate a new array if capacity is exceeded, and this will cause the elements to be copied into the new array.
However, you could store pointers in your MyUsers
slice instead of values:
var MyUsers []*User
If you do this, then there wouldn't be an issue when allocating a new array since the pointers will be moved around, but still pointing to the same underlying User
structs.
Also, as noted in the comments, it'd be better to use a pointer to the Mutex
in the User
struct just in case:
Mutex *sync.RWMutex
But it'd be even better if you design your code so that you never make a copy of a User
object and you always pass the pointers around.