So it's my understanding that on a ReaderWriterLock (or ReaderWriterLockSlim more specifically), both the read and write need acquire a mutex to take the lock. I'd like to optimize the read access of the lock, such that if there are no writes pending, no lock need be acquired. (And I'm willing to sacrifice the performance of writes, add some constraints to the reads, make the first read slow and second fast, etc.. if necessary, as long as the vast majority of the reads are as fast as possible.)
So, how would one do this, or even better, is there a framework or "standard" implementation one could point me to? (Or if I've misunderstood and it's supported already, great!)
So for my piece:
It would seem that if one were to have a counter for the number of readers/writers (protected by Interlocked.Increment), that would be enough for the reader to check if the writer count was non-zero, and only acquire the lock then. (And increment within the lock if acquired.)
Writers would always increment, acquire the lock, spin till the reader count went to 0 (willing to assume readers always finish quickly, or even bypass the reader count entirely in an optimistic scenario), and finally decrement. (It'd be nice to throw in some form priority too when we do block, or potentially clear all pending readers/writers in one pass since I'm only protecting one value, but I'll forgo that for now..)
So.. anyone seen anything similar or have a suggestion? If there's nothing out there after a bit, I'd be happy to throw together an initial implementation and talk more concretely.
What you've described is, at a basic level, already how the reader/writer locks work. They don't need to take a mutex out as the reader/writer lock controls access by using an internal count of readers and writers (and, indeed, a mutex would imply that readers would block each other, whereas in fact multiple concurrent readers are allowed -- that's the whole point of the lock type!).
So yes, there is a framework/standard implementation for this: ReaderWriterLockSlim. I really doubt you'll be able to write a reader/writer lock with better performance than this. In any case -- are you sure that this lock is the root of your performance problems?
I am afraid you are wrong, since ReaderWriterLockSlim is based on spin locking, not on mutexes (you can see this in Reflector).
Related
I am learning more about threading in C#. I just don't understand why would I care about "entering" or "exiting" a ReadLock when it actually doesn't do any locking?
It doesn't do any locking? It gets a read lock.
What happens if something currently has a write lock? You can't read, you need to wait. Everyone who wants to read needs to wait until the write lock is released.
But many objects can have concurrent read locks, since reading doesn't mutate the object and can't cause a race condition.
See https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock for more information.
I'm not entirely sure how best accomplish this multi-threading scenario so any input would be appreciated.
I have one block, that reads data, that several threads can access at once. I have another block that writes data, only one thread can write at any time. Also it can't start writing as long as any thread is reading the data. Is ReaderWriterLockSlim the way to go here, will it wait for the read threads to exit before blocking the thread for writing?
Yes, ReaderWriterLockSlim is perfect for frequent reader/infrequent writer scenarios.
The behaviour is as you guessed - single writer only, writers block until all readers are done, readers cannot access while writer is in process.
Be careful that the time you hold the lock (whether for read or write) is long enough to prevent any concurrent access, and no longer.
Yes, it sounds like ReaderWriterLockSlim is what you want.
A write lock will not be acquired as long as read locks are in place. I suggest you read the documentation for a complete description of the behavior (locking queues, etc):
http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx
Please explain what are the main differences and when should I use what.
The focus on web multi-threaded applications.
lock allows only one thread to execute the code at the same time. ReaderWriterLock may allow multiple threads to read at the same time or have exclusive access for writing, so it might be more efficient. If you are using .NET 3.5 ReaderWriterLockSlim is even faster. So if your shared resource is being read more often than being written, use ReaderWriterLockSlim. A good example for using it is a file that you read very often (on each request) and you update the contents of the file rarely. So when you read from the file you enter a read lock so that many requests can open it for reading and when you decide to write you enter a write lock. Using a lock on the file will basically mean that you can serve one request at a time.
Consider using ReaderWriterLock if you have lots of threads that only need to read the data and these threads are getting blocked waiting for the lock and and you don’t often need to change the data.
However ReaderWriterLock may block a thread that is waiting to write for a long time.
Therefore only use ReaderWriterLock after you have confirmed you get high contention for the lock in “real life” and you have confirmed you can’t redesign your locking design to reduce how long the lock is held for.
Also consider if you can't rather store the shared data in a database and let it take care of all the locking, as this is a lot less likely to give you a hard time tracking down bugs, iff a database is fast enough for your application.
In some cases you may also be able to use the Aps.net cache to handle shared data, and just remove the item from the cache when the data changes. The next read can put a fresh copy in the cache.
Remember
"The best kind of locking is the
locking you don't need (i.e. don't
share data between threads)."
Monitor and the underlying "syncblock" that can be associated with any reference object—the underlying mechanism under C#'s lock—support exclusive execution. Only one thread can ever have the lock. This is simple and efficient.
ReaderWriterLock (or, in V3.5, the better ReaderWriterLockSlim) provide a more complex model. Avoid unless you know it will be more efficient (i.e. have performance measurements to support yourself).
The best kind of locking is the locking you don't need (i.e. don't share data between threads).
ReaderWriterLock allows you to have multiple threads hold the ReadLock at the same time... so that your shared data can be consumed by many threads at once. As soon as a WriteLock is requested no more ReadLocks are granted and the code waiting for the WriteLock is blocked until all the threads with ReadLocks have released them.
The WriteLock can only ever be held by one thread, allow your 'data updates' to appear atomic from the point of view of the consuming parts of your code.
The Lock on the other hand only allows one thread to enter at a time, with no allowance for threads that are simply trying to consume the shared data.
ReaderWriterLockSlim is a new more performant version of ReaderWriterLock with better support for recursion and the ability to have a thread move from a Lock that is essentially a ReadLock to the WriteLock smoothly (UpgradeableReadLock).
ReaderWriterLock/Slim is specifically designed to help you efficiently lock in a multiple consumer/ single producer scenario. Doing so with the lock statement is possible, but not efficient. RWL/S gets the upper hand by being able to aggressively spinlock to acquire the lock. That also helps you avoid lock convoys, a problem with the lock statement where a thread relinquishes its thread quantum when it cannot acquire the lock, making it fall behind because it won't be rescheduled for a while.
It is true that ReaderWriterLockSlim is FASTER than ReaderWriterLock. But the memory consumption by ReaderWriterLockSlim is outright outrageous. Try attaching a memory profiler and see for yourself. I would pick ReaderWriterLock anyday over ReaderWriterLockSlim.
I would suggest looking through http://www.albahari.com/threading/part4.aspx#_Reader_Writer_Locks. It talks about ReaderWriterLockSlim (which you want to use instead of ReaderWriterLock).
after hours of tracking mysterious one or two seconds long lasting "freeze" I finally found out that its ReaderWriterLock(-1). It is server app and the lock here is held for writing to client collection. Im not familiar with locking so I would like to ask if there is not any better/faster way? How about using lock object to lock add/read statements of collection?
This freeze occurs very randomly but its very annoying since it causes lag to all connected clients.
Thanks!
Do you mean AcquireReaderLock(-1)/AcquireWriterLock(-1) or similar? That would suggest that the lock is contended - in which case, fix whichever code is holding the (write) lock at the time. If the delay is literally with the locking class, then maybe ReaderWriterLockSlim would have less overhead. lock would indeed be simpler still (hence cheaper), but doesn't allow the same granularity - it is essentially a mutex lock, so only one thread can play with the object. Contrast a reader/writer lock which allows one writer NAND multiple readers.
To be honest, it sounds as simple as "occasionally, some code will take a write lock (blocking other callers), and take a while to complete". Finding the offending code is a black art, but has little to do with the lock itself.
Under what circumstances should each of the following synchronization objects be used?
ReaderWriter lock
Semaphore
Mutex
Since wait() will return once for each time post() is called, semaphores are a basic producer-consumer model - the simplest form of inter-thread message except maybe signals. They are used so one thread can tell another thread that something has happened that it's interested in (and how many times), and for managing access to resources which can have at most a fixed finite number of users. They offer ordering guarantees needed for multi-threaded code.
Mutexes do what they say on the tin - "mutual exclusion". They ensure that the right to access some resource is "held" by only on thread at a time. This gives guarantees of atomicity and ordering needed for multi-threaded code. On most OSes, they also offer reasonably sophisticated waiter behaviour, in particular to avoid priority inversion.
Note that a semaphore can easily be used to implement mutual exclusion, but that because a semaphore does not have an "owner thread", you don't get priority inversion avoidance with semaphores. So they are not suitable for all uses which require a "lock".
ReaderWriter locks are an optimisation over mutexes, in cases where you will have a lot of contention, most accesses are read-only, and simultaneous reads are permissible for the data structure being protected. In such cases, exclusion is required only when a writer is involved - readers don't need to be excluded from each other. To promote a reader to writer all other readers must finish (or abort and start waiting to retry if they also wish to become writers) before the writer lock is acquired. ReaderWriter locks are likely to be slower in cases where they aren't faster, due to the additional book-keeping they do over mutexes.
Condition variables are for allowing threads to wait on certain facts or combinations of facts being true, where the condition in question is more complex than just "it has been poked" as for semaphores, or "nobody else is using it" for mutexes and the writer part of reader-writer locks, or "no writers are using it" for the reader part of reader-writer locks. They are also used where the triggering condition is different for different waiting threads, but depends on some or all of the same state (memory locations or whatever).
Spin locks are for when you will be waiting a very short period of time (like a few cycles) on one processor or core, while another core (or piece of hardware such as an I/O bus) simultaneously does some work that you care about. In some cases they give a performance enhancement over other primitives such as semaphores or interrupts, but must be used with extreme care (since lock-free algorithms are difficult in modern memory models) and only when proven necessary (since bright ideas to avoid system primitives are often premature optimisation).
Btw, these answers aren't C# specific (hence for example the comment about "most OSes"). Richard makes the excellent point that in C# you should be using plain old locks where appropriate. I believe Monitors are a mutex/condition variable pair rolled into one object.
I would say each of them can be "the best" - depends on the use case ;-)
Simple answer: almost never.
The best type of locking is to not need a lock (no shared mutable state).
If you do need a lock, try and use a Monitor (via a lock statement), unless you have specific needs for something different (in which case see Onebyone's answer
Additionally, prefer ReaderWriteLockSlim to ReaderWriterLock (except in the extremely rare case of requiring the latter's fairness).