Environment: VC6, Windows NT/2000; Should work on 98/Me, but not tested there
Read/write lock is a synchronization mechanism that protects a shared resource in multithreaded environment. Standard mutex (and critical section) object provided by Windows allows only one thread a time to access the resource. In many cases this is overkill. Let's assume we have two or more reader threads that read contents of the resource without changing it, and one or more writer threads that can both read and write to the resource. In this case we can relax rules of protection a little bit:
Many operating systems implement this kind of protection natively. Unfortunately, no native synchronization object provided by Windows offers such a functionality. Nevertheless, read/write locks can be built in Windows based on available standard synchronization objects.
This article offers two lock objects:
class KReadWriteLock { KReadWriteLock( KReadWriteLock const& ); // not implemented void operator=( KReadWriteLock const& ); // not implemented public: KReadWriteLock(); bool LockRead( DWORD Timeout = INFINITE ); bool LockWrite( DWORD Timeout = INFINITE ); bool UnlockRead( DWORD Timeout = INFINITE ); bool UnlockWrite( DWORD Timeout = INFINITE ); };
Locking rules for KReadWriteLock are as follows:
KReadWriteLock prefers neither writers nor readers. I.e. if both writer(s) and reader(s) are waiting for the lock, any of them can grab the lock when it is released by previous owner.
KReadWriteLock is recursive, but not upgradable. This means that
class KReadWriteLockEx { KReadWriteLockEx( KReadWriteLockEx const& ); // not implemented void operator=( KReadWriteLockEx const& ); // not implemented public: KReadWriteLockEx(); bool LockRead( DWORD Timeout = INFINITE ); bool LockWrite( DWORD Timeout = INFINITE ); bool UnlockRead( DWORD Timeout = INFINITE ); bool UnlockWrite( DWORD Timeout = INFINITE ); };
KReadWriteLockEx offers more flexibility: it is both recursive and upgradable. That is, if you have a read lock, you can safely acquire write lock and vice versa. Further more, you can even overlap locks:
KReadWriteLockEx Lock;
Lock.LockRead();
Lock.LockWrite();
Lock.UnlockRead();
Lock.UnlockWrite();
KReadWriteLockEx prefers writers over readers. That is, if a writer is waiting on the lock, no new readers are allowed to access the resource. Existing readers can continue to use the resource until they release the lock. This prevents so-called "writer starvation".
For all this flexibility, KReadWriteLockEx is slower than KreadWriteLock, takes more memory and its implementation is more complex. This is because KReadWriteLockEx must manually keep track of what threads are currently owning the lock. In case of KReadWriteLock this job is done by the operating system.
NOTE: neither KReadWriteLock nor KReadWriteLockEx will work between processes. Although, they can be extended to do so.
class KReadLock { KReadLock(KReadWriteLock& Lock, bool bInitialLock = true); ~KReadLockTempl(); bool Lock(DWORD Timeout = INFINITE); bool Unlock(DWORD Timeout = INFINITE); };Use of methods is pretty straightforward and similar to CSingleLock. Note, that unlike CSingleLock, helper classes will acquire the lock in constructor by default.