Is this code locking only on if
statement, or for both lines of code?
lock (LockObject)
if (instance == null)
instance = Instance();
lock
locks an entire block. Since it's not followed by curly braces ({}
), it locks an implicit block - the if
statement. Here, the same logic applies - if
executes a block if the condition is met. Since it is also not followed by curly braces, it implicitly has a block that contains a single statement. In other words, the given code is equivalent to:
lock (LockObject) {
if (instance == null) {
instance = Instance();
}
}
Lock is translated by the C# compiler to Monitor.Enter
and Monitor.Exit
. This C# code
static void Main(string[] args)
{
lock (LockObject)
if (instance == null)
instance = Instance();
Console.WriteLine(instance == null);
}
gives the IL code below, which clearly shows that Monitor.Exit
(L_0036) is called after instance
is assigned (L_0026).
Both lines of code are locked.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] bool flag,
[1] object obj2,
[2] bool flag2)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: ldsfld object TestLock.Program::LockObject
L_0008: dup
L_0009: stloc.1
L_000a: ldloca.s flag
L_000c: call void [mscorlib]System.Threading.Monitor::Enter(object, bool&)
L_0011: nop
L_0012: ldsfld object TestLock.Program::instance
L_0017: ldnull
L_0018: ceq
L_001a: ldc.i4.0
L_001b: ceq
L_001d: stloc.2
L_001e: ldloc.2
L_001f: brtrue.s L_002b
L_0021: call object TestLock.Program::Instance()
L_0026: stsfld object TestLock.Program::instance
L_002b: leave.s L_003d
L_002d: ldloc.0
L_002e: ldc.i4.0
L_002f: ceq
L_0031: stloc.2
L_0032: ldloc.2
L_0033: brtrue.s L_003c
L_0035: ldloc.1
L_0036: call void [mscorlib]System.Threading.Monitor::Exit(object)
L_003b: nop
L_003c: endfinally
L_003d: nop
L_003e: ldsfld object TestLock.Program::instance
L_0043: ldnull
L_0044: ceq
L_0046: call void [mscorlib]System.Console::WriteLine(bool)
L_004b: nop
L_004c: ret
.try L_0003 to L_002d finally handler L_002d to L_003d
}
if
statement can't be executed alone, it needs block after it for the true
-case of expression, so, as @Mureinik already said, lock
locks your entire initialization block. You can even write it like this:
lock (LockObject) if (instance == null) instance = Instance();
However, this is not recommended to write your code without curly braces in such cases as it is very confusing and hard to debug. Also note that lock
statement is a syntax sugar for Monitor
class usage, and your code is compiled into this:
try
{
Monitor.Enter(LockObject);
if (instance == null)
{
instance = Instance();
}
}
finally
{
Monitor.Exit(LockObject);
}
And I want to note that for initialization logic you can use a Lazy<T>
class, which is thread-safe, and uses not so heavy constructures as Monitor
, and can perform faster than your code. Code will be like this:
// field in class
Lazy<Instance> lazyInstance = new Lazy<Instance>(() => Instance());
//usage in code
var instanceValue = lazyInstance.Value;
It's locking all of the code. Here {
and }
for if
are just omitted.
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