views:

232

answers:

2

Should I make a single NSLock instance in the application delegate, to be used by all classes? Or is it advisable to have each class instantiate its own NSLock instance as needed?

Would the locking work in the second case, if I, for example, had access to a managed object context that is spread across two view controllers?

A: 

If multiple objects access your object only to read its contents, then you do not need a lock at all. If at least one of the objects accesses your object to write/update its contents, then it does not matter if the other objects access your object to read or write/update it: in this case you need a lock.

Now, in order to correctly protect your object (in a critical section of code where multiple objects may access it), you must use the SAME LOCK INSTANCE which must then be shared by ALL of the possible objects accessing the object you are willing to protect.

If your application need to protect an object that may be accessed simultaneously by the majority of the classes, then having a single lock instance is fine. If you want better performances (especially if the number of simultaneous accesses to your object is high), then you can have multiple locks. Each lock will be responsible for allowing/denying access to a specific attribute/field of your object. This way, several objects may access your object changing a different attribute/field simultaneously. You are basically incrementing the number of concurrent operations on your object. However, each lock MUST STILL be shared among the other objects that will access the object you are protecting.

Having a lock instance for each controller simply does NOT work; this will NOT protect your object from concurrent accesses from other objects in different threads. NSLock is implemented using POSIX pthread mutexes, so it must be used in exactly the same way. This is also clearly stated in the NSLock documentation:

Warning: The NSLock class uses POSIX threads to implement its locking behavior. When sending an unlock message to an NSLock object, you must be sure that message is sent from the same thread that sent the initial lock message. Unlocking a lock from a different thread can result in undefined behavior.

So, in order to preserve the critical section semantics, it is the same thread that acquired the lock that is responsible for releasing it when done. Note also that the locking mechanism is intended for fast operations only, i.e. you should acquire a lock only for a short period of time before releasing it. If you need to wait for an unpredictable amount of time, then you need a different synchronization mechanism, namely a condition variable which is available through the NSCondition class.

Hope this helps.

unforgiven
What is clear from the documentation is that the same lock used for locking within a particular thread is also to be used to unlock. However, it is not clear from the documentation for `NSLock` that the same lock instance has to be used across all classes. I am also reading that if I use `[myLock lock]` more than once, before issuing a corresponding `[myLock unlock]`, then the thread will freeze. So I am still uncertain if I need to restrict the instantiation and scope of `myLock` to a particular class/thread being called, or whether I can use an application-wide `myLock` without problems.
Alex Reynolds
I suggest reading the documentation for posix pthread mutexes. You will see that a mutex (an NSLock) must be shared among all of the threads that will access the object you want to protect. Note that I am not saying that it must be shared globally across all of the threads: only among the ones will access your object. What you say about locking two times in a row is correct: unless the mutex is recursive a problem will arise. To use recursive mutexes you must use a NSRecursiveLock object. See the "Threading Programming Guide" documentation, in particular the "Synchronization" section.
unforgiven
A: 

You should not use locks with Core Data. That documentation is probably out of date. Ideally you should have one context per thread and let the context handle the locking of its underlying NSPersistentStoreCoordinator. This is considered the only safe way to use Core Data in a multi-threaded application currently.

Marcus S. Zarra