A sequentially-consistent system guarantees you that its behavior will always look the same as some sequential execution of the reads and writes involved, consistent with the relative order of reads and writes in the program that each processor is executing. It doesn't make any guarantees about what order two operations executed by different processors will appear.
It's certainly possible that you'll end up with:
P1: store(x, 1)
P2: store(y, 1)
P1: load(y) // 1
P2: load(x) // 1
and neither processor will increment z. However, it's also perfectly reasonable for the system to settle on:
P1: store(x, 1)
P1: load(y) // 0
P1: store(z, z+1)
P2: store(y, 1)
P2: load(x) // 1
With 'z' ending up set to 1. You'll need to add locking if you want a deterministic result. A sequentially-consistent system DOES guarantee you that z will never be 2: there's no way to reorder the writes so that both processes load a value of 0 and increment z without violating sequential consistency.
In contrast, a causually-consistent system does not guarantee that its behavior will always look the same as a single sequential execution of the reads and writes involved. It's perfectly okay for different processors to see other people's writes in different orders unless those writes are causually related. You may very well end up with:
// P1's local history
P1: store(x, 1)
P1: load(y) // 0
P1: store(z, z+1)
P2: store(y, 1)
// P2's local history
P2: store(y, 1)
P2: load(x) // 0
P2: store(z, z+1)
P1: store(x, 1)
And a final value of 2 for z. What causual consistency DOES guarantee you is that a third process executing:
if (z == 2)
print(x, y)
will never print a 0:
- If P3 loads/prints x and y, the loads must occur after its load of z
- If the load of z sees a value of 2, it must occur after P1 and P2's increments of the same in P3's local history
- P1's store(x, 1) must appear before its increment of z; similarly for P2/y
By transitivity, any local history for P3 that includes the print statement MUST see ones for x and y.