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

C++MultithreadingConcurrency

C++ Problem Overview


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.

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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.

Solution 3 - C++

I'd recommend not to rely on any compiler or architecture in this case.
Whenever you have a mix of readers and writers (as opposed to just readers or just writers) you'd better sync them all. Imagine your code running an artificial heart of someone, you don't really want it to read wrong values, and surely you don't want a power plant in your city go 'boooom' because someone decided not to use that mutex. Save yourself a night-sleep in a long run, sync 'em.
If you only have one thread reading -- you're good to go with just that one mutex, however if you're planning for multiple readers and multiple writers you'd need a sophisticated piece of code to sync that. A nice implementation of read/write lock that would also be 'fair' is yet to be seen by me.

Solution 4 - C++

Imagine that you're reading the variable in one thread, that thread gets interrupted while reading and the variable is changed by a writing thread. Now what is the value of the read integer after the reading thread resumes?

Unless reading a variable is an atomic operation, in this case only takes a single (assembly) instruction, you can not ensure that the above situation can not happen. (The variable could be written to memory, and retrieving the value would take more than one instruction)

The consensus is that you should encapsulate/lock all writes individualy, while reads can be executed concurrently with (only) other reads

Solution 5 - C++

> 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.

In the general case, that is potentially every architecture. Every architecture has cases where reading concurrently with a write will result in garbage. However, almost every architecture also has exceptions to this rule.

It is common that word-sized variables are read and written atomically, so synchronization is not needed when reading or writing. The proper value will be written atomically as a single operation, and threads will read the current value as a single atomic operation as well, even if another thread is writing. So for integers, you're safe on most architectures. Some will extend this guarantee to a few other sizes as well, but that's obviously hardware-dependant.

For non-word-sized variables both reading and writing will typically be non-atomic, and will have to be synchronized by other means.

Solution 6 - C++

If you don't use prevous value of this variable when write new, then:

  You can read and write integer variable without using mutex. It is because integer is base type in 32bit architecture and every modification/read of value is doing with one operation.

But, if you donig something such as increment:

myvar++;

  Then you need use mutex, because this construction is expanded to myvar = myvar + 1 and between read myvar and increment myvar, myvar can be modified. In that case you will get bad value.

Solution 7 - C++

While it would probably be safe to read ints on 32 bit systems without synchronization. I would not risk it. While multiple concurrent reads are not a problem, I do not like writes to happen at the same time as reads.

I would recommend placing the reads in the Critical Section too and then stress test your application on multiple cores to see if this is causing too much contention. Finding concurrency bugs is a nightmare I prefer to avoid. What happens if in the future some one decides to change the int to a long long or a double, so they can hold larger numbers?

If you have a nice thread library like boost.thread or zthread then you should have read/writer locks. These would be ideal for your situation as they allow multiple reads while protecting writes.

Solution 8 - C++

This may happen on 8 bit systems which use 16 bit integers.

If you want to avoid locking you can under suitable circumstances get away with reading multiple times, until you get two equal consecutive values. For example, I've used this approach to read the 64 bit clock on a 32 bit embedded target, where the clock tick was implemented as an interrupt routine. In that case reading three times suffices, because the clock can only tick once in the short time the reading routine runs.

Solution 9 - C++

In general, each machine instruction goes thru several hardware stages when executing. As most current CPUs are multi-core or hyper-threaded, that means that reading a variable may start it moving thru the instruction pipeline, but it doesn't stop another CPU core or hyper-thread from concurrently executing a store instruction to the same address. The two concurrently executing instructions, read and store, might "cross paths", meaning that the read will receive the old value just before the new value is stored.
To resume: you do need the mutex for both read and writes.

Solution 10 - C++

Both reading / writing to variables with concurrency must be protected by a critical section (not mutex). Unless you want to waste your whole day debugging.

Critical sections are platform-specific, I believe. On Win32, critical section is very efficient: when no interlocking occur, entering critical section is almost free and does not affect overall performance. When interlocking occur, it is still more efficient, than mutex, because it implements a series of checks before suspending the thread.

Solution 11 - C++

Depends on your platform. Most modern platforms offer atomic operations for integers: Windows has InterlockedIncrement, InterlockedDecrement, InterlockedCompareExchange, etc. These operations are usually supported by the underlying hardware (read: the CPU) and they are usually cheaper than using a critical section or other synchronization mechanisms.

See MSDN: [InterlockedCompareExchange][1]

I believe Linux (and modern Unix variants) support similar operations in the pthreads package but I don't claim to be an expert there.

[1]: http://msdn.microsoft.com/en-us/library/ms683560%28VS.85%29.aspx "InterlockedCompareExchange"

Solution 12 - C++

If a variable is marked with the volatile keyword then the read/write becomes atomic but this has many, many other implications in terms of what the compiler does and how it behaves and shouldn't just be used for this purpose.

Read up on what volatile does before you blindly start using it: http://msdn.microsoft.com/en-us/library/12a04hfd(VS.80).aspx

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHongliView Question on Stackoverflow
Solution 1 - C++peterchenView Answer on Stackoverflow
Solution 2 - C++Gunther PiezView Answer on Stackoverflow
Solution 3 - C++DmitryView Answer on Stackoverflow
Solution 4 - C++NomeNView Answer on Stackoverflow
Solution 5 - C++jalfView Answer on Stackoverflow
Solution 6 - C++Vitaly DyatlovView Answer on Stackoverflow
Solution 7 - C++iainView Answer on Stackoverflow
Solution 8 - C++starblueView Answer on Stackoverflow
Solution 9 - C++harrymcView Answer on Stackoverflow
Solution 10 - C++SadSidoView Answer on Stackoverflow
Solution 11 - C++LaszloGView Answer on Stackoverflow
Solution 12 - C++Jeff TuckerView Answer on Stackoverflow