C# Lock statements
When a thread tries to enter a critical section and obtain a lock, what is it actually doing?
I'm asking this because I usually create an object (of type object) which will serve for locking purposes only. Consider the following: I want to write a method which accepts a collection, and an object which will serve as the locking object so the whole collection manipulation inside that method will be declared inside the critical section which will be locked by that given object.
Should I pass that locking object using "ref" or is passing a reference copy of that object is enough? In other words - since the lock statement is used with reference types only, does the mechanism check the value of the referenced o开发者_JAVA技巧bject, or does it check the pointer's value? because obviously, when passing an object without "ref", I actually get a copy of the reference, and not the reference itself.
Here's a typical pattern that you can follow for locking. Basically, you can create a locking object that is used to lock access to your critical section (which, as @Hans said, is not protecting the object that you're working on -- it just handles the lock).
class ThreadSafe
{
static readonly object _locker = new object();
static int _val1, _val2;
static void Go()
{
lock (_locker)
{
if (_val2 != 0) Console.WriteLine (_val1 / _val2);
_val2 = 0;
}
}
}
This example was from Joseph Albahari's online book on threading. It provides an excellent overview of what's going on when you create a lock
statement and some tips/tricks on how to best optimize for it. Definitely highly recommended reading.
Per Albahari, again, the lock
statement translates in .NET 4 as:
bool lockTaken = false;
try
{
Monitor.Enter (_locker, ref lockTaken);
// Do your stuff...
}
finally { if (lockTaken) Monitor.Exit (_locker); }
It's actually safer than a straight Monitor.Enter
and then calling Monitor.Exit
in your finally
, which is why it was added in .NET 4.
It's enough to lock the object
without passing ref
. What lock
actually does is, call
Monitor.Enter on the beginning of the block and Monitor.Exit on exit.
Hope this helps.
MSDN says here about lock
Use Enter to acquire the Monitor on the object passed as the parameter. If another thread has executed an Enter on the object but has not yet executed the corresponding Exit, the current thread will block until the other thread releases the object. It is legal for the same thread to invoke Enter more than once without it blocking; however, an equal number of Exit calls must be invoked before other threads waiting on the object will unblock.
which means it's not about the reference or pointer it is about the actual object which is pointed by the reference so you won't need to pass as ref
simple pass by reference will work
Regarding what actually happens inside the lock see answer to this question which says
"The lock statement is translated by C# to the following:"
var temp = obj;
Monitor.Enter(temp);
try
{
// body
}
finally
{
Monitor.Exit(temp);
}
Should I pass that locking object using "ref" or is passing a reference copy of that object is enough?
Probably neither. If you have some resource that is not thread-safe, the best option usually is to access that resource directly only from one class, which has a lock object as a field (or you can lock directly on the resource). If you pass the lock object to others, it's hard to make sure that the code will still work properly, e.g. locking is done when it should and there are no deadlocks.
But if you really want to pass the lock object, you don't need to use ref
, as others have pointed out. Locking is done on the instance of the object, not on the variable containing reference to it.
精彩评论