Thread Safety in ios

ref – http://quellish.tumblr.com/post/101831085987/implementing-thread-safety-in-cocoa-ios

You have just introduced “threads” to your application, because doing everything on the same thread as the UI was starting to noticeably bog things down. At some point you have run into strange crashes or exceptions. Your application no longer behaves as it used to.

One problem you have likely introduced is sharing data between threads. If two threads can be changing something, you’re asking for trouble. The common solution to this problem is to protect access to that resource: to introduce some form of locking on the resource to only allow one thing to touch it at a time.

In Objective-C there are a number of ways to deal with this, with the most common being use of the @synchronized keyword to protect access to a block of code.
The @synchronized keyword

In Objective-C the @synchronized keyword provides an implicit reentrant lock on a unique identifier for a block of code. For example:

This prevents the code inside that statement from executing until any contention on foo is released. This prevents more than one thread from using that lock at a time.

Because the lock is implicit, there is overhead even when the lock is not in contention. If nothing else is accessing the lock, the system still has to find the lock, which takes time. The lock used is recursive, which is more expensive than a non-recursive lock. Additionally, the @syncrhonized keyword does put into place an exception handler that adds some additional overhead. Because @synchronized uses a lock, it’s possible to use it in ways that result in a deadlock.

@synchronized is a blunt instrument, and unfortunately is often used to hammer away “threading problems”.

Lock-free synchronization

The point of synchronizing access to something is to ensure only one thing can access it at a time. On MacOS and iOS we have the concept of queues for concurrency. A serial queue is an ideal way to protect access without the potential costs of locking. If every access to the protected resource occurs through a serial queue the problem is solved – the queue guarantees that only one thing will be accessing it at a time. For example:

Because the serial queue executes work in FIFO order and only one block at a time, the statement is protected. Unlike using a lock, this can’t deadlock.

This is the preferred method for protecting a resource shared between threads on MacOS and iOS.
Not needing synchronization at all

The recommended solution for this problem is not to have it all. If you are sharing a mutable resource between threads, question why.

You may have noticed that much of Foundation is class clusters that provide both immutable (i.e. NSString) and immutable (i.e. NSMutableString) versions of a given class cluster object. An immutable object cannot be changed after it is created – it has a finite, determinate state and life cycle. Because of this, immutable objects are safe to exchange betweeen threads – there is no problem with two threads changing the object at the same time. Mutable objects should be considered unsafe unless they are specifically documented to be safe to access across threads.

Unfortunately few objective-c developers follow this practice even though it is one of the SOLID object oriented design principles. The developer will run into a mysterious crash or exception, google it, and after reading a few Stack Overflow articles start stamping @synchronized all over the place.
In practice

Implementing a reader-writer lock or the equivalent in objective-c is easy, and easy to get wrong. You will commonly see something like this:

Which isn’t effective for several reasons. Each method is synchronizing on self, so ANY access to self or it’s members will cause a contention.

By overriding the accessor methods for the foo property and accessing the instance variables directly the application may not be correctly managing the memory associated with foo.

A better implementation would be:

This uses a private property to ensure correct memory management. Note that because it’s of type id, we retain it strongly. If foo were a type that is a class cluster, we would prefer declaring it as copy rather than strong. That would ensure that even if it were set with a mutable object, the accessor would make an immutable copy before setting it. Instead of using @synchronized to provide an implicit lock, we’re using a serial queue to protect access to the private property. Note that in the case where we return the value in the getter we use dispatch_sync to make the calling thread wait for the result. This does use a lock, but the lock does not have most of the disadvantages of @synchronized and is only used in the reading case. An alternative would be to pass a block to be executed when the value is available:

This not only removes the need to block, but executes asynchronously.

Leave a Reply