views:

108

answers:

2

I have found possible slowdown in my app so I would have two questions:

  1. What is the real difference between simple locking on object and reader/writer locks? E.g. I have a collection of clients, that change quickly. For iterations should I use readerlock or the simple lock is enough?
  2. In order to decrease load, I have left iteration (only reading) of one collection without any locks. This collection changes often and quickly, but items are added and removed with writerlocks. Is it safe (I dont mind occassionally skipped item, this method runs in loop and its not critical) to left this reading unsecured by lock? I just dont want to have random exceptions.
+2  A: 

No, your current scenario is not safe.

In particular, if a collection changes while you're iterating over it, you'll get an InvalidOperationException in the iterating thread. You should obtain a reader lock for the whole duration of your iterator:

  • Obtain reader lock
  • Iterate over collection
  • Release reader lock

Note this is not the same as obtaining a reader lock for each step of the iteration - that won't help.

As for the difference between reader/writer locks and "normal" locks - the idea of a reader/writer lock is that multiple threads can read at the same time, but only one thread can write (and only when no-one is reading). In some cases this can improve performance - but it increases the complexity of the solution too (in terms of getting it right). I'd also advise you to use ReaderWriterLockSlim from .NET 3.5 if you possibly can - it's much more efficient than the original ReaderWriterLock, and there are some inherent problems with ReaderWriterLock IIRC.

Personally I normally use simple locks until I've proved that lock contention is a performance bottleneck. Have you profiled your application yet to find out where the bottleneck is?

Jon Skeet
Thanks! Just one simple question - I have seen some codes where there were different instances of Reader or writer locks for each collection. Why? Would not be enough to have one and call it above needed object?For your question: Yes, but the profiler results were not much specific. But from you information I think I need to use reader/writer locks instead of simple locks, due to multithread nature of the app where there is often needed reading ad writing to the same collection.
Tomas
reader/writer locks work best when there are a)lots of different threads acessing, b) most operations are reads. If you only have let's say one thread reading and one (or more) thread writing, and the collection is updated frequently then reader/writer lock brings no benefit at all.
Egor
Sometimes having multiple locks can give you more fine-grained control, typically through nesting. For example read operations can acquire first a read lock, and then a write lock, while write operations just need to aquire a write lock. This means that if there are a bunch of threads waiting for a read lock and one waiting for a write lock, the write operation gets to skip all the queued read ops. However, these sorts of tricks often make everything so confusing that the questionable performance gains aren't worth it. One more thing you should worry about more after everything actually works.
Egor
What Egor said, basically :) The more locks you have, the more efficient you *may* be able to make the code - but the less chance you'll have of getting it right in the first place.
Jon Skeet
+2  A: 

Ok first about the reading iteration without locks thing. It's not safe, and you shouldn't do it. Just to illustrate the point in the most simple way - you're iterating through a collection but you never know how many items are in that collection and have no way to find out. Where do you stop? Checking the count every iteration doesn't help because it can change after you check it but before you get the element.

ReaderWriterLock is designed for a situation where you allow multiple threads have concurrent read access, but force synchronous write. From the sounds of your application you don't have multiple concurrent readers, and writes are just as common as reads, so the ReaderWriterLock provides no benefit. You'd be better served by classic locking in this case.

In general whatever tiny performance benefits you squeeze out of not locking access to shared objects with multithreading are dramatically offset by random weirdness and unexplainable behavior. Lock everything that is shared, test the application, and then when everything works you can run a profiler on it, check just how much time the app is waiting on locks and then implement some dangerous trickery if needed. But chances are the impact is going to be small.

“We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.A good programmer will not be lulled into complacency by such reasoning, he will be wise to look carefully at the critical code; but only after that code has been identified” - Donald Knuth

Egor