I'm an experienced .NET programmer and stretching my legs with iOS. One of my favorite multi-threading constructs in .NET is the ReaderWriterLock. It allows for multiple readers or a single writer. The one feature that I am really missing in iOS is that the locks are re-entrant. That is, reader threads can acquire the read lock multiple times as long as they release it the same number of times. Likewise, the single writer thread can acquire the lock multiple times as long as it releases the lock by an equivalent number.
I've looked into the iOS Framework and none of the constructs appear to offer the same support including the re-entrancy. I also looked into the pthread library. I found rwlock, but it does not allow re-entrancy.
Is there anything on iOS that allows for re-entrant read-write locks?
The ReadWriteLock is a pair of associated locks, one for read-only operations and one for writing. Whereas, the ReentrantLock is a re-entrant mutual exclusion Lock with the same behavior as the implicit monitor lock accessed using synchronized methods and statements, but with some more extended capabilities.
As per Javadoc, ReentrantLock is a mutual exclusive lock, similar to implicit locking provided by synchronized keyword in Java, with extended features like fairness, which can be used to provide lock to longest waiting thread.
Lock is an interface. It defines a set of methods that all locks should have. ReentrantLock is a concrete class that implements the Lock interface.
A reader/writer lock pair allows any number of readers to "own" the read lock at the same time, OR it allows one writer to own the write lock, but it never allows a reader and a writer at the same time, and it never allows more than one writer at the same time.
If any thread is using the write lock of Reentrant Read Write Lock pair, read lock on resource is not allowed. We use write lock from Reentrant Read Write Lock pair with write code. Acquiring the lock before, then calling the write task in try block and releasing the write lock in finally block.
A reentrant lock is one where a process can claim the lock multiple times without blocking on itself. It's useful in situations where it's not easy to keep track of whether you've already grabbed a lock. If a lock is non re-entrant you could grab the lock, then block when you go to grab it again, effectively deadlocking your own process.
Read lock allows multiple thread to acquire lock in read method when all the synchronizing threads are using only the read lock of the Reentrant Read Write Lock pair. If any thread is using the write lock of Reentrant Read Write Lock pair, read lock on resource is not allowed.
Acquiring the lock before, then calling the read task in try block and releasing the read lock in finally block. Read lock allows multiple thread to acquire lock in read method when all the synchronizing threads are using only the read lock of the Reentrant Read Write Lock pair.
Yes, the @synchronized
directive is reentrant. See Using the @synchronized Directive in the Threading Programming Guide and also Threading in the ObjC Programming Language.
That said, you should almost never use this in iOS. In most cases you can avoid locks of all kinds, let alone very heavyweight (slow) locks like reentrant locks. See the Concurrency Programming Guide, and particularly the section Migrating Away from Threads, for extensive information the queue-based approaches that iOS favors over manual thread management and locking.
For example, the reader/writer lock works like this using Grand Central Dispatch:
- (id)init {
...
_someObjectQueue = dispatch_queue_create("com.myapp.someObject",
DISPATCH_QUEUE_CONCURRENT);
}
// In iOS 5 you need to release disptach_release(_someObjectQueue) in dealloc,
// but not in iOS 6.
- (id)someObject {
__block id result;
dispatch_sync(self.someObjectQueue, ^{
result = _someObject;
});
return result;
}
- (void)setSomeObject:(id)newValue {
dispatch_barrier_async(self.queue, ^{
_someObject = newValue;
});
This approach allows unlimited parallel readers, with exclusive writers, while ensuring that writers never starve and that writes and reads are serialized, all while avoiding any kernel calls unless there is actual contention. That's all to say it's very fast and simple.
When a reader comes along, you queue a request to read the value, and wait for it to process. When a writer comes along, it queues a barrier request to update it, which requires that no other requests from that queue are currently running. With this construct, the developer doesn't need to manage any locks. Just put things on the queue in the order you want them to run.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With