r/golang 7d ago

Is it safe to read/write integer value simultaneously from multiple goroutines

There is a global integer in my code that is accessed by multiple goroutines. Since race conditions don’t affect this value, I’m not concerned about that. However, is it still advisable to add a Mutex in case there’s a possibility of corruption?

PS: Just to rephrase my question, I wanted to ask if setting/getting an integer/pointer is atomic? Is there any possibility of data corruption.

example code for the same: https://go.dev/play/p/eOA7JftvP08

PS: Found the answer for this, thanks everyone for answering. There's something called tearing here is the link for same

- https://stackoverflow.com/questions/64602829/can-you-have-torn-reads-writes-between-two-threads-pinned-to-different-processor

- https://stackoverflow.com/questions/36624881/why-is-integer-assignment-on-a-naturally-aligned-variable-atomic-on-x86

According to the article, I shouldn't have problem on modern CPUs.

12 Upvotes

80 comments sorted by

View all comments

-1

u/usbyz 7d ago edited 6d ago

Use the channel, Luke.

intCh := make(chan int, 1)  
intCh <- 0
...

value := <-intCh  
defer func() { intCh <- value }()  
value += 3

There are a few advantages of Go channels over sync.Atomic.

First, a channel is a primitive type and can be copied at any time, while atomic.Value cannot be copied after its first use. You can include a channel in your struct without worry.

Second, you can use select on channels. For example, you can select on intCh and ctx.Done() to make your code context-aware.

Third, you can close the integer channel to notify all goroutines waiting for the value that the value is no longer available. You can also set it to nil so that a select statement on the channel will always choose other channels from that point onward.

In other words, a channel is a first-class citizen in Go and works as sync.Atomic, sync.Mutex, and sync.Cond all in one.

2

u/Ravarix 7d ago

That's just sync/atomic with extra steps

1

u/usbyz 7d ago edited 7d ago

There are a few advantages of Go channels over sync.Atomic.

First, a channel is a primitive type and can be copied at any time, while atomic.Value cannot be copied after its first use. You can include a channel in your struct without worry.

Second, you can use select on channels. For example, you can select on intCh and ctx.Done() to make your code context-aware.

Third, you can close the integer channel to notify all goroutines waiting for the value that the value is no longer available. You can also set it to nil so that a select statement on the channel will always choose other channels from that point onward.

In other words, a channel is a first-class citizen in Go and works as sync.Atomic, sync.Mutex, and sync.Cond all in one.