views:

952

answers:

7

The thing is I've been using the lock statement to protect a critical part of my code, but now, I realize I could allow concurrent execution of that critical code is some conditions are met.
Is there a way to condition the lock?

+1  A: 
Action doThatThing = someMethod;

if (condition)
{
  lock(thatThing)
  {
     doThatThing();
  }
}
else
{
  doThatThing();
}
David B
Thanks everybody... I guess there's nothing out of the box of the lock statement
sebastian
A: 

I'm guessing you've got some code that looks a little like this:

private Monkey GetScaryMonkey(int numberOfHeads){
    Monkey ape = null;        
    lock(this) {
        ape = new Monkey();
        ape.AddHeads(numberOfHeads);            
    }
    return ape;
}

To make this conditional couldn't you just do this:

private Monkey GetScaryMonkey(int numberOfHeads){
    if ( numberOfHeads > 1 ) {
         lock(this) {
            return CreateNewMonkey( numberOfHeads );          
        }
    }
    return CreateNewMonkey( numberOfHeads );
}

Should work, no?

noocyte
lock(this) is bad: http://srtsolutions.com/blogs/billwagner/archive/2007/09/09/why-lock-this-is-bad.aspx
spoulson
@spoulson - just what I was going to say ;-p
Marc Gravell
+7  A: 

I think that question cries "race condition!". What if the condition turns from true to false shortly after the check, but before a thread enters the critical section of code? Or while a thread is in the process of executing it?

Tomalak
The condition "comes" with the thread...
sebastian
@Seba - and what if thread A decides it doesn't need the lock, but thread B later decides that it does? You have a curious combination of locked/non-locked. Hence a reader/writer lock seems a good fit.
Marc Gravell
@Tomalak - I'm with you here...
Marc Gravell
This would be okay if the condition was invariant over the application's lifecycle... perhaps a configuration.
David B
Keep reading the other answers for a better solution, in this post. Double-checked locking should be the answer u're after.
Pure.Krome
+2  A: 

Actually, to avoid a race condition, I'd be tempted to use a ReaderWriterLockSlim here - treat concurrent access as a read lock, and exclusive access as a write lock. That way, if the conditions change you won't end up with some inappropriate code still executing blindly in the region (under the false assumption that it is safe); a bit verbose, but (formatted for space):

        if (someCondition) {
            lockObj.EnterReadLock();
            try { Foo(); }
            finally { lockObj.ExitReadLock(); }
        } else {
            lockObj.EnterWriteLock();
            try { Foo(); }
            finally { lockObj.ExitWriteLock(); }
        }
Marc Gravell
Small addition: This is not possible prior to .NET 3.5, ReaderWriterLockSlim is freshly introduced.
Tomalak
True; before that there is ReaderWriterLock
Marc Gravell
+2  A: 
bool locked = false;
if (condition) {
    Monitor.Enter(lockObject);
    locked = true;
}
try {
    // possibly critical section
}
finally {
    if (locked) Monitor.Exit(lockObject);
}

EDIT: yes, there is a race condition unless you can assure that the condition is constant while threads are entering.

marijne
The point is: it isn't just while entering - if the condition changes, then that probably means that code that hasn't taken a lock is running at risk.
Marc Gravell
If you know that all your threads have already entered then it's safe to change the condition. But it's not likely that you'd know that for sure.
marijne
Sorry, but I have to disagree; if the meaning of "condition" is such that the threads should be synchronized, then it has to apply to the entire execution of the critical section - not just the entry point. If any thread entered without a lock, and synchronization becomes needed, you are in trouble.
Marc Gravell
Look, it's not a completely thread-safe condition, but in the unlikely event that you know that no more threads will be entering before the rest have cleared the CS you are OK. It's no different from the accepted answer above so it's clear the OP has in fact made such assumptions.
marijne
Oh, I agree it is identical to the accepted answer; I'm just remarking on your point about "entering" - it is wider than that. btw, the downvote isn't from me, but since it matches the accepted, I'll +1 to even it out; so if you check you should be +8 up over all...
Marc Gravell
(as in: -1 vote and +1 vote is +8 rep points)
Marc Gravell
+3  A: 

I'm no threading expert, but it sounds like you might be looking for something like this (double-checked locking). The idea is to check the condition both before and after acquiring the lock.

private static object lockHolder = new object();

if (ActionIsValid()) {
  lock(lockHolder) {
    if (ActionIsValid()) {
       DoSomething();    
    }
  }
}
A: 

Use Double-checked locking pattern, as suggested above. that's the trick IMO :)

make sure you have your lock object as a static, as listed in not.that.dave.foley.myopenid.com's example.

Pure.Krome