Home > Back-end >  How to ensure the comparison result still hold in multi-threading?
How to ensure the comparison result still hold in multi-threading?

Time:12-16

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.

  • Related