Multithreading: Which lock object should I use?





3.00/5 (8 votes)
Which lock object should I use?
The lock
keyword locks a specified code block so two threads can't process the same code block at the same time. When one threads exits the locked block, another thread can enter the locked code block. The Monitor
class offers the same functionality, but you specify the start and end of the locked code block with Monitor.Enter
and Monitor.Exit
. For both techniques, you need a variable to lock on. A common pattern is to lock on this for instance data in a class or typeof
(type) for static
data.
using System;
using System.Threading;
public class LockObject
{
private static int counter = 0;
public static void MonitorIncrement()
{
Monitor.Enter(typeof(LockObject));
counter++;
Monitor.Exit(typeof(LockObject));
}
public static void LockIncrement()
{
lock (typeof(LockObject))
{
counter++;
}
}
}
The problem with this is, this typeof
(type) could also be the lock
object in an entirely different synchronization block outside the class in an unrelated code block. The result would be that two completely different synchronization blocks that synchronize two different sets of data can block each other. The same thing can happen if you use a string
as a lock
variable, because all the string
s refer to the same instance. These problems can be solved with a private
read-only field to lock on!
public class LockObject
{
private static int counter = 0;
private readonly static object syn = new object();
public static void MonitorIncrement()
{
Monitor.Enter(syn);
counter++;
Monitor.Exit(syn);
}
public static void LockIncrement()
{
lock (syn)
{
counter++;
}
}
}
The lock
object is private
so it can't be used by code blocks outside the class as lock
object! The read-only attribute prevents the variable from changes. The problem in the samples can also be solved by using Interlocked.Increment
! It is only used to show the problem on a simple example!