What is does
A little known feature of .NET is the synchronized keyword. The keyword can be used on methods and it ensures:
- Instance method – can be executed in a single thread on the instance (different instances are not synchronized). Equivalent to lock(this).
- Static method – can be executed in a single thread. Equivalent to lock(typeof(TypeName)).
Usage in C#
If you’ll look at the C# specification you’ll see that there’s no mention of this keyword. The reason is that the keyword is an IL keyword and not a C# one. In order to instruct the compiler to mark the method as synchronized, we can use the MethodImplAttibute with Synchronized MethodImplOptions. For example:
[MethodImpl(MethodImplOptions.Synchronized)]
public void MethodWithSyncAttribute()
{
}
The IL result
Using synchronized keyword
The MethodWithSyncAttribute() looks in IL:
.method public hidebysig instance void MethodWithSyncAttribute() cil managed synchronized
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
}
It is very clear that this method has no explicit lock instructions like Monitor.Enter for example. Yet, it’ll still behave the same as if we had used a lock block around the method body.
Using lock block
The previous method is equivalent to the next:
public void MethodWithExplicitLock()
{
lock(this)
{
}
}
This method translates into:
.method public hidebysig instance void MethodWithExplicitLock() cil managed
{
// Code size 36 (0x24)
.maxstack 2
.locals init ([0] bool ‘s__LockTaken0’,
[1] class Sync.Logger CS$2$0000,
[2] bool CS$4$0001)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
.try
{
IL_0003: ldarg.0
IL_0004: dup
IL_0005: stloc.1
IL_0006: ldloca.s ‘s__LockTaken0’
IL_0008: call void [mscorlib]System.Threading.Monitor::Enter(object,
bool&)
IL_000d: nop
IL_000e: nop
IL_000f: nop
IL_0010: leave.s IL_0022
} // end .try
finally
{
IL_0012: ldloc.0
IL_0013: ldc.i4.0
IL_0014: ceq
IL_0016: stloc.2
IL_0017: ldloc.2
IL_0018: brtrue.s IL_0021
IL_001a: ldloc.1
IL_001b: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_0020: nop
IL_0021: endfinally
} // end handler
IL_0022: nop
IL_0023: ret
}
As can be seen, the lock block translates naturally into a try/finally block with calls to Montior.Enter and Monitor.Leave.
Summary
The synchronized keyword is an IL keyword that synchronizes the marked method calls. It causes the method to behave in an equivalent way to the one where the whole body is surrounded with lock block. It is interesting to note that locking instructions are generated only during JIT when using the keyword.
The bottom line is that for C# developers it mostly provides another syntactic sugar for defining trivial lock.