views:

779

answers:

7

I'm still confused... When we write some thing like this:

Object o = new Object();
var resource = new Dictionary<int , SomeclassReference>();

...and have two blocks of code that lock o while accessing resource...

//Code one
lock(o)
{
  // read from resource    
}

//Code two
lock(o)
{
  // write to resource
}

Now, if i have two threads, with one thread executing code which reads from resource and another writing to it, i would want to lock resource such that when it is being read, the writer would have to wait (and vice versa - if it is being written to, readers would have to wait). Will the lock construct help me? ...or should i use something else?

(I'm using Dictionary for the purposes of this example, but could be anything)

There are two cases I'm specifically concerned about:

  1. two threads trying to execute same line of code
  2. two threads trying to work on the same resource

Will lock help in both conditions?

A: 

And that should work assuming that you only have one process involved. You will want to use a "Mutex" if you want that to work across more then one process.

Oh, and the "o" object, should be a singleton or scoped across everywhere that lock is needed, as what is REALLY being locked is that object and if you create a new one, then that new one will not be locked yet.

JasonRShaver
+2  A: 

Both blocks of code are locked here. If thread one locks the first block, and thread two tries to get into the second block, it will have to wait.

Brian Genisio
There are two cases ...1) two thredas trying to execute same line of code 2) two threads trying to work on the same resourcewill lock help in both conditions
SilverHorse
+5  A: 

Yes, using a lock is the right way to go. You can lock on any object, but as mentioned in other answers, locking on your resource itself is probably the easiest and safest.

However, you may want use a read/write lock pair instead of just a single lock, to decrease concurrency overhead.

The rationale for that is that if you have only one thread writing, but several threads reading, you do not want a read operation to block an other read operation, but only a read block a write or vice-versa.

Now, I am more a java guy, so you will have to change the syntax and dig up some doc to apply that in C#, but rw-locks are part of the standard concurrency package in Java, so you could write something like:

public class ThreadSafeResource<T> implements Resource<T> {
    private final Lock rlock;
    private final Lock wlock;
    private final Resource res;

    public ThreadSafeResource(Resource<T> res) {
        this.res = res;
        ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
        this.rlock = rwl.readLock();
        this.wlock = rwl.writeLock();
    }

    public T read() {
        rlock.lock();
        try { return res.read(); }
        finally { rlock.unlock(); }
    }

    public T write(T t) {
        wlock.lock();
        try { return res.write(t); }
        finally { wlock.unlock(); }
    }
}

If someone can come up with a C# code sample...

Varkhan
any idea how to apply that read write lock pair?
SilverHorse
Locking on the resource is not a good idea if that resource is visible outside of his internal code. That could lead to deadlocks or race conditions. it is recommended that you lock on a private object to prevent that scenario.
Erich Mirabal
Well, in that case you *do* want to prevent other concurrency-aware code to mess up with your (not-thread-safe) resource while you are doing stuff on it, so you *do* want to lock on it.
Varkhan
A: 

The way you have it implemented is an acceptable way to do what you need to do. One way to improve your way of doing this would be to use lock() on the dictionary itself, rather than a second object used to synchronize the dictionary. That way, rather than passing around an extra object, the resource itself keeps track of whether there's a lock on it's own monitor.

Using a separate object can be useful in some cases, such as synchronizing access to outside resources, but in cases like this it's overhead.

Dan Monego
+2  A: 

The lock (o) { ... } statement is compiled to this:

Monitor.Enter(o)
try { ... }
finally { Monitor.Exit(o) }

The call to Monitor.Enter() will block the thread if another thread has already called it. It will only be unblocked after that other thread has called Monitor.Exit() on the object.

Mark Cidade
+6  A: 

Most of the other answers address your code example, so I'll try to answer you question in the title.

A lock is really just a token. Whoever has the token may take the stage so to speak. Thus the object you're locking on doesn't have an explicit connection to the resource you're trying to synchronize around. As long as all readers/writers agree on the same token it can be anything.

When trying to lock on an object (i.e. by calling Monitor.Enter on an object) the runtime checks if the lock is already held by a thread. If this is the case the thread trying to lock is suspended, otherwise it acquires the lock and proceeds to execute.

When a thread holding a lock exits the lock scope (i.e. calls Monitor.Exit), the lock is released and any waiting threads may now acquire the lock.

Finally a couple of things to keep in mind regarding locks:

  • Lock as long as you need to, but no longer.
  • If you use Monitor.Enter/Exit instead of the lock keyword, be sure to place the call to Exit in a finally block so the lock is released even in the case of an exception.
  • Exposing the object to lock on makes it harder to get an overview of who is locking and when. Ideally synchronized operations should be encapsulated.
Brian Rasmussen
did not understand this "Exposing the object to lock on makes it harder to get an overview of who is locking and when. Ideally synchronized operations should be encapsulated"Thanks
SilverHorse
If the object used for locking is accessible outside the type, it becomes harder to identify who's locking. On the other hand if all locking is encapsulated, you can easily get an overview of the locking.
Brian Rasmussen
its not clear . u mean the object 'o' that i ve used shud be strictly within a type right?
SilverHorse
Yes, if possible.
Brian Rasmussen
+2  A: 

Will lock help in both conditions? Yes.

Does lock(){} lock a resource, or does it lock a piece of code?

lock(o)
{
  // read from resource    
}

is syntactic sugar for

Monitor.Enter(o);
try
{
  // read from resource 
}
finally
{
  Monitor.Exit(o);
}

The Monitor class holds the collection of objects that you are using to synchronize access to blocks of code. For each synchronizing object, Monitor keeps:

  1. A reference to the thread that currently holds the lock on the synchronizing object; i.e. it is this thread's turn to execute.
  2. A "ready" queue - the list of threads that are blocking until they are given the lock for this synchronizing object.
  3. A "wait" queue - the list of threads that block until they are moved to the "ready" queue by Monitor.Pulse() or Monitor.PulseAll().

So, when a thread calls lock(o), it is placed in o's ready queue, until it is given the lock on o, at which time it continues executing its code.

mbeckish
@mbeckish : Should be 'finally' and not 'catch' surely. Sorry to nitpick :)
Andy
@Andy - Yes. Updated answer.
mbeckish