I read recently about memory barriers and the reordering issue and now I have some confusion about it.
Consider the following scenario:
private object _object1 = null;
private object _object2 = null;
private bool _usingObject1 = false;
private object MyObject
{
get
{
if (_usingObject1)
{
return _object1;
}
else
{
return _object2;
}
}
set
{
if (_usingObject1)
{
_object1 = value;
}
else
{
_object2 = value;
}
}
}
private void Update()
{
_usingMethod1 = true;
SomeProperty = FooMethod();
//..
_usingMethod1 = false;
}
At Update
method; is the _usingMethod1 = true
statement always executed before getting or setting the property? or due to reordering issue we can not guarantee that?
Should we use volatile
like
private volatile bool _usingMethod1 = false;
If we use lock;
can we guarantee then every statement within the lock will be executed in order like:
private void FooMethod()
{
object locker = new object();
lock (locker)
{
x = 1;
y = a;
i++;
}
}
A memory barrier is an instruction that requires the processor to apply an ordering constraint between memory operations that occur before and after the memory barrier instruction in the program. Such instructions are also known as memory fences in other architectures.
So locking a mutex and immediately unlocking it acts as a memory barrier, albeit a horribly inefficient one since it forces serial execution.
Memory barriers are used to provide control over the order of memory accesses. This is necessary sometimes because optimizations performed by the compiler and hardware can cause memory to be accessed in a different order than intended by the developer.
Fencing is the process of isolating a node of a computer cluster or protecting shared resources when a node appears to be malfunctioning.
The subject of memory barriers is quite complex. It even trips up the experts from time to time. When we talk about a memory barrier we are really combining two different ideas.
A memory barrier that creates only one of two is sometimes called a half-fence. A memory barrier that creates both is sometimes called a full-fence.
The volatile
keyword creates half-fences. Reads of volatile fields have acquire semantics while writes have release semantics. That means no instruction can be moved before a read or after a write.
The lock
keyword creates full-fences on both boundaries (entry and exit). That means no instruction can be moved either before or after each boundary.
However, all of this moot if we are only concerned with one thread. Ordering, as it is perceived by that thread, is always preserved. In fact, without that fundamental guarentee no program would ever work right. The real issue is with how other threads perceive reads and writes. That is where you need to be concerned.
So to answer your questions:
From a single thread's perspective...yes. From another thread's perspective...no.
It depends. That might work, but I need to have better understanding of what you are trying to acheive.
From another thread's perspective...no. The reads and writes are free to move around within the boundaries of the lock. They just cannot move outside those boundaries. That is why it is important for other threads to also create memory barriers.
The volatile keyword doesn't accomplish anything here. It has very weak guarantees, it does not imply a memory barrier. Your code doesn't show another thread getting created so it is hard to guess if locking is required. It is however a hard requirement if two threads can execute Update() at the same time and use the same object.
Beware that your lock code as posted doesn't lock anything. Each thread would have its own instance of the "locker" object. You have to make it a private field of your class, created by the constructor or an initializer. Thus:
private object locker = new object();
private void Update()
{
lock (locker)
{
_usingMethod1 = true;
SomeProperty = FooMethod();
//..
_usingMethod1 = false;
}
}
Note that there will also be a race on the SomeProperty assignment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With