Replies: 6 comments 1 reply
-
The memory barrier can help in very rare cases. Some processors may not execute instructions in the sequence in which we have defined them. And it is important for us to have the code to create and initialize (in the case of injection via methods, fields or properties) an instance: XyzDependency _singletonXyzDependency41Temp;
_singletonXyzDependency41Temp = new XyzDependency();
accumulator44.Add(_singletonXyzDependency41Temp); is always executed before the code when we update the shared state in a class field:
This is unlikely, but the ECMA specification has a weaker memory model. So it is theoretically possible that some version of .NET on some processor could start behaving this way. Then, some thread could start using the object before it is fully initialized.
|
Beta Was this translation helpful? Give feedback.
-
You can disable thread synchronization. Here is an example. |
Beta Was this translation helpful? Give feedback.
-
I know the purpose of MemoryBarriers. Usually you use them with lock free programming. I can see you have locks around the shared state. That is why I asked if they are really needed. lock statement generates implicit memory barrier at the end of lock block. lock (_lock)
{
if (_root._singletonXyzDependency41 == null)
{
XyzDependency _singletonXyzDependency41Temp;
_singletonXyzDependency41Temp = new XyzDependency();
accumulator44.Add(_singletonXyzDependency41Temp);
Thread.MemoryBarrier(); // <-- I wonder why you need it here
_root._singletonXyzDependency41 = _singletonXyzDependency41Temp; // must have some concerns about this line, that can't see why. You can rely on the implicit memory barrier at the end of the lock
} // <-- Thread.MemoryBarrier(); here by the .net generated assembly code
} Here analysis: same for perBlockAbcDependency4 - it is local variable so no locking is required. Inside the lock you create another local variable that doesn't require locks So the only shared state I see is You have shared state here Thread.MemoryBarrier() // <-- It make sense to add Thread.MemoryBarrier() here, as another thread might already have created the instance
// but you might won't see it in this thread. The behavior won't be errornous and the only drawback of the missing
// memory barrier is that you might obtain the lock without a need. Once you got the lock you'll see the value
// So this is a trade off for always have some small overhead for the memory barrier, before the first if statement or
// or bigger overhead but not always (only if another thread happens to assign the value just before the if statement)
if (_root._singletonXyzDependency41 == null)
{
lock (_lock)
{
if (_root._singletonXyzDependency41 == null) Here information for the topic https://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility
|
Beta Was this translation helpful? Give feedback.
-
Yes, you're right `lock' creates barriers. Our barrier is for another. Imagine this scenario:
|
Beta Was this translation helpful? Give feedback.
-
This is indeed the case in your code. But in the general case the state of local variables should be thread-safe as well. For example, if we have delegates that can be called in a thread different from the thread of object composition creation. Imagine that you are not injecting instance creation directly, but injecting a |
Beta Was this translation helpful? Give feedback.
-
Are you saying that memory synchronization code is part of a template for code generations and there could be different code generated around that locks, depending what you inject? |
Beta Was this translation helpful? Give feedback.
-
I was looking at the generated code here https://github.com/DevTeam/Pure.DI/blob/master/readme/accumulators.md
May be I am missing something, but is that memory barrier really needed here? You have implicit memory barrier at the end of the lock statement.
Also, why there is lock around accumulator44 if it is a local variable?
Beta Was this translation helpful? Give feedback.
All reactions