Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it necessary to have atomic flags in multi-threaded code?

I was wondering if it is really necessary to have atomic flags in a multi-threaded code. For this problem, I focus on a common situation in multi-thread code: stopping threads by setting a flag.

Let's assume following pseudo-code:

is_running = 1;
create_threads(stopper_thread, running_thread_A, running_thread_B, running_thread_C);


stopper_thread         running_thread_A        running_thread_B        running_thread_C
-------------------------------------------------------------------------------------------
 if (x)             |  while(is_running) {   | while(is_running) {   | while(is_running) {
    is_running = 0; |  }                     | }                     | }

In this pseudo-code, all running_thread_x threads use common variable is_running to check if they are running or not. When we want to stop them in stopper_thread, we just set is_running to 0. this means that is_running is a shared resource between threads. In a lot of coding samples, people use atomic variables (for example std::atomic_flag in C++) for is_running flag or access to this variable in a critical section to provide mutual exclusion in accessing this variable.

But is synchronizing this flag necessary?

I somehow believe that in situations similar to aforementioned example where there is just stopping operation as single or multi stopper thread(s), it is practically not necessary to synchronize access to this flag.

Why?

Because as far as I know, even if we have simultaneous access to is_running flag in multiple threads when we want to stop threads, this access does not prevent from setting this flag from 1 to 0 by stopper thread. What happens is that this change may not be reflected in running threads immediately. But is this important? I think not, because if we don't read value 0 from is_running in current iteration of running threads, you will finally read it after few more iterations and thread will be stopped finally. So setting this flag will finally stops all running threads, but stopping may be delayed a little.

What do you think about my argument? Is my argument correct? or I may be missing a situation that my argument fails?

like image 501
Afshin Avatar asked Dec 15 '25 15:12

Afshin


1 Answers

What happens is that this change may not be reflected in running threads immediately.

What happens is that this is an undefined behaviour. The compiler is allowed to do pretty much anything with non-synchronized code. For example it is allowed to rewrite

while(is_running) { }

into

auto running = is_running;
while(running) { }

when condition doesn't change inside while body.

And so it will loop forever, regardless of future values of is_running. This rewrite is not allowed when is_running is declared as atomic.

Moreover without atomic even if the compiler doesn't rewrite this code, the CPU is still allowed to do it (it may read the stale value from cache, not memory).

The reason people use atomics is to avoid UB. If you do multi threading then you have to use synchronization primitives whenever you synchronize threads. There's no escape.

like image 104
freakish Avatar answered Dec 19 '25 05:12

freakish



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!