4

I have a struct with some integers field, like

struct s {
    int a;
    int b;
    int c;
    int max;
};
struct s mystruct = {0, 0, 0, 0}; // Global var

And then I have N threads that sometimes have to do things like ++ or -- on the first three fields, and sometimes have to read them and then update the max field. Do I have to use a mutex here? And if yes, is it needed only when reading/updating max or always? Why? If I just increment or decrement the first three fields, does it matter if a thread runs before another?

Davide R.
  • 207
  • 2
  • 8

2 Answers2

10

The universal rule of concurrency is this: If a memory location is accessed concurrently in a non-atomic way, and not all accesses are reads, then the accesses must be ordered. Ordering can be imposed either by serializing access through locking a mutex, or by some lower-level operation like an ordered atomic access or an ordered fence.

The only time you're allowed to have unordered accesses if all of the accesses are reads. E.g. multiple threads can read a (non-atomic) constant without ordering.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • The technical concepts that define "ordering" are "happens-before" and "synchronizes-with". – Kerrek SB Jun 01 '16 at 13:16
  • The accesses do *not* have to ordered. If the accesses are atomic with `memory_order_relaxed`, they are not ordered. – EOF Jun 01 '16 at 17:03
5

You need to synchronize the access to the variables if the operations you are performing are not atomic. Consider a scenario of a single variable a having initial value of, say, 5. Now we have two threads T1 and T2, both want to increment it. How increment operation is broken down, in case it is not atomic (just an example, it could be some other way)?

1) Read a value into a temp location.
2) Increment and write the temp location back into a.

Now consider a scenario T1 is performing this operation first, and then pre-empted by T2 after completing step 1 only:

T1: 1) Read a into temp1 => temp1=5
T2: 1) Read a into temp2 => temp2=5
T2: 2) Write temp2+1 into a => a=6
T1: 2) Write temp1+1 into a => a=6

So the final value of a will be 6 and not 7 as you would expect.

PfhorSlayer
  • 1,337
  • 9
  • 14
Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61