For the following code I get a compile time error, *
'int' is not a reference type as required by the lock statement
int i = 0;
lock(i);
But no errors for this:
int i = 0;
Monitor.Enter(i);
I understand that a value type shouldn't be used for locking due to the complications arising due to boxing. But, then why does it work with Monitor.
Remember that when you pass a value type for a parameter of type object , it gets boxed (wrapped) into a reference type. This makes it a brand-new object each time this happens. You cannot lock a value type because it doesn't have a sync root record.
CSharp Online Training Both Monitor and lock provides a mechanism that synchronizes access to objects. lock is the shortcut for Monitor. Enter with try and finally. Lock is a shortcut and it's the option for the basic usage.
Avoid using 'lock keyword' on string object String object: Avoid using lock statements on string objects, because the interned strings are essentially global in nature and may be blocked by other threads without your knowledge, which can cause a deadlock.
Lock is not blocking threads. It is locking some instance of an object. And each thread which tries to access it is blocked.
The reason why is that lock is a language construct and compiler chooses to impose extra semantics on the expression. Monitor.Enter is simply a method call and the C# compiler does not special case the call in any way and hence it goes through normal overload resolution and boxing.
You should definitely not use Monitor.Enter
on an int
. The reason it works is because the int
is boxed, so unless you store a reference to the boxed value, you will be locking on a temporary object, which means that you cannot call Monitor.Exit
without getting an exception.
The recommended way to do locking is to create a private readonly object
and lock on that. For a static method you can use a private static object
.
The specification for the compiler defines the behaviour of lock like so:
The compile time type of the expression of a lock statement shall be a reference-type or a > type parameter (§25.1.1) known to be a reference type. It is a compile-time error for the compile time type of the expression to denote a value-type.
It then defines what it is equivalent to so long as it compiles
Since Monitor.Exit is just a method call without any constraints it will not prevent the compiler automatically boxing the int and going on its merry (and very) wrong way.
lock
is NOT simply syntactic sugar in the same way foreach
is not simply syntactic sugar. The resulting IL transform is not presented to the rest of the code as if that was what had been written.
In foreach
it is illegal to modify the iteration variable (despite there being nothing at the IL level in the resulting output code that would prevent this). In lock the compiler prevents compile time known value types, again despite the resulting IL not caring about this.
As an aside:
In theory the compiler could be 'blessed' with intimate knowledge of this (and other) methods so that it spotted obvious cases of this happening but fundamentally it is impossible to always spot this at compile time (consider an object passed in from another method, assembly or via reflection) so bothering to spot any such instances would likely be counter productive.
The more the compiler knows about the internals of the API the more problems you will have if you wish to alter the API in future.
It is possible for example that an overload of Monitor.Enter() could be added which took an int and locked on a process wide mutex associated with the int value.
This would conform to the specifications of monitor (even though it would likely be hideous) but would cause massive problems for the older compiler still merrily preventing an operation which had become legal.
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