Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to read an integer variable that's being concurrently modified without locking?

Suppose that I have an integer variable in a class, and this variable may be concurrently modified by other threads. Writes are protected by a mutex. Do I need to protect reads too? I've heard that there are some hardware architectures on which, if one thread modifies a variable, and another thread reads it, then the read result will be garbage; in this case I do need to protect reads. I've never seen such architectures though.

This question assumes that a single transaction only consists of updating a single integer variable so I'm not worried about the states of any other variables that might also be involved in a transaction.

like image 790
Hongli Avatar asked Aug 29 '09 09:08

Hongli


People also ask

Is reading a variable thread safe?

No this operation is not inherently thread safe. Even though the variable is not currently being written to, previous writes to the variable may not yet be visible to all threads. This means two threads can read the same value and get different results creating a race condition.

Do I need a mutex for reading?

Unless you use a mutex or another form of memory barrier. So if you want correct behavior, you don't need a mutex as such, and it's no problem if another thread writes to the variable while you're reading it. It'll be atomic unless you're working on a very unusual CPU.


2 Answers

You ask a question about reading a variable and later you talk about updating a variable, which implies a read-modify-write operation.

Assuming you really mean the former, the read is safe if it is an atomic operation. For almost all architectures this is true for integers.

There are a few (and rare) exceptions:

  • The read is misaligned, for example accessing a 4-byte int at an odd address. Usually you need to force the compiler with special attributes to do some misalignment.
  • The size of an int is bigger than the natural size of instructions, for example using 16 bit ints on a 8 bit architecture.
  • Some architectures have an artificially limited bus width. I only know of very old and outdated ones, like a 386sx or a 68008.
like image 41
Gunther Piez Avatar answered Sep 21 '22 18:09

Gunther Piez


atomic read
As said before, it's platform dependent. On x86, the value must be aligned on a 4 byte boundary. Generally for most platforms, the read must execute in a single CPU instruction.

optimizer caching
The optimizer doesn't know you are reading a value modified by a different thread. declaring the value volatile helps with that: the optimizer will issue a memory read / write for every access, instead of trying to keep the value cached in a register.

CPU cache
Still, you might read a stale value, since on modern architectures you have multiple cores with individual cache that is not kept in sync automatically. You need a read memory barrier, usually a platform-specific instruction.

On Wintel, thread synchronization functions will automatically add a full memory barrier, or you can use the InterlockedXxxx functions.

MSDN: Memory and Synchronization issues, MemoryBarrier Macro

[edit] please also see drhirsch's comments.

like image 171
peterchen Avatar answered Sep 20 '22 18:09

peterchen