The synchronized keyword

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.