0

I'm working on a project that uses multiples threads which can access a shared struct containing multiple variables.

To avoid concurrency problems, I use mutexes whenever a write is made to one of the variables inside the shared struct. Right now I use a different mutex for each variable, so that I avoid locking a thread for trying to write to a variable that is not being written to, but I'd like to know if there is a better way to avoid this problem.

Also, considering that my struct has uint16_t variables, is there any additional measures I should take to guarantee data consistency across threads because of memory alignment and multiple variables stored on the same data register?

Thanks.

kuro64
  • 21
  • 4
  • "...but I'd like to know if there is a better way to avoid this problem." - Using **single mutex** for all variables, because it is **simpler**. Note, that before optimize performance, you need to measure that performance first, as usual. As for accessing different structure fields from different mutexes, see that question: https://stackoverflow.com/questions/47008183/race-condition-when-accessing-adjacent-members-in-a-shared-struct-according-to. – Tsyvarev May 02 '18 at 20:59

1 Answers1

1

You didn't say what language. I'm going to pretend it's C++.

I use a different mutex for each variable...

That sounds suspicious.

Your threads communicate with each other by updating shared state. A mutex, when correctly used, does two things:

  • It ensures that after thread A updates the shared state, other threads will be able to see what was changed, and
  • It ensures that the other threads will not see the change until the change is complete.

If a thread can meaningfully change the state of the program by writing a single uint16_t value, then you don't need a mutex for that. Just change the type of the variable to atomic<uint16_t>. Making it atomic will ensure that other threads can see the change. And, you don't have to worry about the completeness. That's trival: Every other thread must either see the old value or the new value. There's no other possibilities.

You need a mutex when the situation is more complicated. Suppose you have three variables:

int a, b, c;

And, suppose that there's a rule, that a+b+c must always equal zero. A rule like that is called an invariant. Now, there's no way a thread can legally change just one of those three variables. But, if it tries to change two of them in a row, there's always the possibility that some other thread could look at them at just the wrong moment, and it could see them in an inconsistent state (i.e., in a state that breaks the invariant.)

Making the variables atomic won't fix the problem. And, making a separate mutex for each variable won't fix the problem. You need one mutex whose purpose is to protect the invariant. You need every thread that updates a, b, and/or c to lock that one mutex whenever it's making a change; and you need every thread that expects the invariant to be true to lock the same mutex whenever it looks at them.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57