Suppose there are 3 threads,
Thread 1 and 2 will increase or decrease a global variable X atomically.
thread 1:
atomic_increase(X)
thread 2:
atomic_decrease(X)
Thread 3 will check if the X is greater than some predefined value and do things accordingly.
thread 3:
if( X > 5 ) {... logic 1 ...}
else {... logic 2 ....}
I think the atomic_xxx
operations are not enough. They can only synchronize the modifications between thread 1 and 2.
What if X
is changed by thread 1 or 2 after thread 3 finishes the comparison and enters logic 1
.
Do I have to use a mutex to synchronize all the 3 threads when modifying or reading the X
?
ADD 1
BTW, logic 1 and logic 2 don't modify the X.
CodePudding user response:
In short yes, reads also need to be synchronized in some way, otherwise the risk of inconsistent reads is real. A read performed between the read and write of atomic_increase
will be inconsistent.
However if logic 1
or logic 2
do stuff to X
, your problems doesn't seem to stop right there. I think then you need the concept of a transaction, where it starts with a read (the X > 5
thing) and then ends with a write (logic 1
or logic 2
).
CodePudding user response:
Yes, And the Answer is happens before
link, Lets say Thread-1
started executing atomic_increase
method. It will hold the lock
and enter the synchronized
block to update X
.
private void atomic_increase() {
synchronized (lock) {
X = X 1; // <-- Thread-1 entered synchronized block, yet to update variable X
}
}
Now, for Thread-3
to run the logic, it needs to read the variable X
, and if it is not synchronized
(on the same monitor), the variable X
read can be an old value since it may not yet updated by Thread-1
.
private void runLogic() {
if (X > 5) { // <-- Reading X here, can be inconsistent no
happens-before between atomic_increase and runLogic
} else {
}
}
We could have prevented this by maintaining a happens-before
link between atomic
operation and run_logic
method. If the runLogic
is synchronized
(on the same monitor) , then it would have to wait until the variable X
to be updated by the Thread-1
. So we are guaranteed to get the last updated value of X
private void runLogic() {
synchronized (lock) {
if (X > 5) { // <-- Reading X here, will be consistent, since there
is happens-before between atomic_increase and runLogic
} else {
}
}
}
CodePudding user response:
The answer depends on what your application does. If neither logic 1 nor logic 2 modifies X
, it is quite possible that there is no need for additional synchronization (besides using an atomic_load to read X
).
I assume you use intrinsics for atomic operations, and not simply an increment in a mutex (or in a synchronized block in Java). E.g. in Java there is an AtomicInteger
class with methods such as 'incrementAndGet' and 'get'. If you use them, there is probably no need for additional synchronization, but it depends what you actually want to achieve with logic 1 or logic 2.
If you want to e.g. display a message when X > 5
, then you can do it. By the time the message is displayed the value of X
may have already changed, but it remains the fact, that the message was triggered by X
being greater than 5 for at least some time.
In other words, without additional synchronization, you have only the guarantee that logic 1 will be called if X
becomes greater than 5, but there is no guarantee that it will remain so during execution of logic 1. It may be ok for you, or not.