iConcepts: 12 Myths About Concurrency in Objective-C By Jiva

12 Myths About Concurrency in Objective-C By Jiva 

There’s lots of cases where new Objective-C programmers incorrectly apply threads or misunderstand their use. I often see code from well intentioned folks who use threads where they absolutely don’t need them. Consequently, I thought I would put together this list of 12 myths to help guide you along the way to the proper use of threads in your apps.

Myth #1: Threads will make my app faster!

This is probably the most common newbie programmer mistake. Thinking that threads will make your app faster.

They won’t. Your code will execute at exactly the same rate regardless of whether it’s threaded or not. The key advantage that threads provide is that, in the case of a multiple-core machine, they allow you to do two things at once. This can speed processing of certain operations, but only in cases where the different operations are parallelizable. That is, in cases where the operations do not rely on each other for any portion of their calculations.

Even in cases where your operations are parallelizable, if you do not have multiple cores, your code will actually run slower if you add threads, since the processor will be doing more context switches to handle your threads.

https://i0.wp.com/www.random-ideas.net/Dropbox/Threading.png

So, in fact, the cases where threading will actually improve the performance of your app are confined to a relatively small subset. Still, this subset is important and encompasses many common use cases. Unfortunately, too often threads are applied in areas where they are not needed and where they will not significantly improve performance. The effort required to get threaded code right is substantial, and you need to weigh the benefit to bullshit ratio involved. You should also always profile your app before adding threads. You may find that the use case which you think is causing you problems in fact is not. Measure twice and cut once.

Myth #2: Threads will make my code simpler.

Writing thread safe code is hard. It’s seductive to think that if you could spin specific operations off into background threads, they become easier to manage because you no longer have to deal with things like callbacks, and asynchronicity.

The fact is, that adding threading to a complex app will only make it much, much more complicated. There’s an old saying that goes:

Without threads you have n problems. But with threads you have n+1 problems.

Or, as I like to say:

With threads you have n! problems!

Not only will you still have to deal with all your normal coding problems, you’ll also have to deal with things like locking, synchronization, queuing changes. Your code really becomes a lot more difficult to manage when you add threads.

Myth #3: I need threads to solve my problems with blocking I/O.

This common misconception comes from the fact that there are several synchronous methods on some of the foundation classes such as NSString and NSData which allow you to download content from the Internet. These methods, such as+stringWithContentsOfURL: are tremendously convenient, but block the current thread. Using them is as simple as:

NSString *myStr = [NSString 
                     stringWithContentsOfURL:urlToMyData];

Obviously, you’d never want to block the main thread, so a conscientious coder will often (wrongly) imagine that spinning this operation off into a background thread is a good way to still use these convenient methods. The fact is that this is a bad idea.

Threads can only be terminated when they are checking some kind of control variable and can thus be triggered to exit, such as:

while(keepRunning)
{
    NSString *myStr = [NSString 
                     stringWithContentsOfURL:urlToMyData];
}

Since the +stringWithContentsOfURL method blocks, the keepRunning variable will never get checked. So how do you stop this thread? The fact is, you don’t. There is simply no way to terminate a blocked thread.

This issue alone should be enough to make you walk away, but in case its not, consider that you also have no way to specify a timeout for these methods, you can no longer properly handle complex handshaking such as from authentication, and you lose the ability to interpret status codes that might be returned when accessing the resource.

Because of all this, you should never use these methods.

You might be saying to yourself right now “But I don’t need to worry about those things, I have a very simple use case, and my data will always be available.”

The answer is no. Never use these methods! there’s no need to! The Foundation classes, NSURLConnection and NSURLRequest provide more than enough functionality to do the job properly without blocking and without using threads.

Before I came to Objective-C I wrote a lot of C++, most of it networking code. I know how hard it is to get networking code right. One surprising lesson I learned is that even when writing services that had to handle many incoming connections, threads are almost always still the wrong answer. If you imagine a case where you’re handling hundreds of connections, you really don’t want to spawn and manage hundreds of threads. You’re really much better treating your sockets as a pool and handling I/O on them that way.

Much research has been done on this subject, but one of the best resources I have found is from Dan Kegel in his article, “The C10K problem.” You can go read those docs, and bleed yourself into socket code for the next 5 years of your career, or you can take my word for it: Apple knows what they’re doing here, and they got it right.NSURLConnection and NSURLRequest will handle 90% of the networking needs of most developers.

(How to handle the other 10%? That’s another post!)

A lot of developers will also make the mistake of prematurely optimizing networking code like this when they have large resources they have to download. They imagine that they need to run even NSURLConnection in a background thread. This couldn’t be further from the truth.

NSURLConnection is extremely efficient at downloading even very large files. I’ve never seen a file it couldn’t handle. It will not block your runloop. Your own code, processing those large files, may block the runloop and need to be threaded, but the actual downloading will not.

Myth #4: NSOperation means I don’t have to think about threading issues.

Just because you’re using NSOperation doesn’t mean your code is magically thread safe. NSOperation provides no thread safety facilities whatsoever. It simply provides an easier way to spawn and manage threads by abstracting away the details of the thread itself. All you need to provide is the code that actually does the work. NSOperation does the rest for you.

What this means in terms of our discussion here is that everything I’ve said about writing thread safe code still applies and code you write as part of your NSOperations still needs to be thread safe.

Myth #5: Atomic properties makes my code thread safe

As we’ve already seen, thread safety is a complex subject full of dark alleys and pitfalls. At it’s most fundamental level, even just reading a value from memory can be fraught with peril. When making your classes thread safe, you have to account for both these low level fundamental operations as well as higher logical level relationships, such as setting different variables within your class.

Atomic properties addresses only the issue of accessing your property values. An atomic property insures that the setter of a property cannot modify the property while it is being retrieved. Basically, when you use atomic properties, the accessors generated by the compiler wind up looking something (roughly) like the following:

 //@property(retain) UITextField *userName;
 //Generates roughly

 - (UITextField *) userName {
     UITextField *retval = nil;
     @synchronized(self) {
         retval = [[userName retain] autorelease];
     }
     return retval;
 } - (void) setUserName:(UITextField *)userName_ {
     @synchronized(self) {
       [userName_ retain];
       [userName release];
       userName = userName_;
     }
 }

(via objective c – Atomic vs nonatomic properties – Stack Overflow)

When using an atomic property, you will never run into the circumstance where you receive a corrupt value from the getter due to the setter changing the value while you are getting it. This doesn’t mean that the setter won’t change the value after you have received it, and so, thus, it does not fully insure thread safety. It only addresses one part of the full thread safety picture.

Myth #6: Serial operation queues are not threaded.

Just because you’ve made an NSOperationQueue serial by setting it’smaxConcurrentOperationCount to 1 doesn’t mean that the operations you add to that queue will not be run in a background thread. In fact, with the exception of the built-in queue you get from the NSOperationQueue class method, +mainQueue(NSOperationQueue Class Reference) all queues will run in a background thread. In the case of the +mainQueue it actually returns the queue associated with the main thread of your application and runs it on that queue. The main queue is serial.

Myth #7: GCD makes threading easy.

Along these same lines, GCD also does not make working with threads any safer or easier than any other thread mechanism. GCD’s role in Objective-C threading is actually for the purposes of managing thread pools. It’s job is to correctly determine the appropriate number of threads to use for your hardware based on the number of processors and cores your machine has. Although you can use the low level GCD API to add your operations directly to GCD, NSOperation provides a higher level abstraction to the very same mechanism, and is, generally, the preferred way to do it.

Myth #8: Threads Can Be Terminated Safely From Outside The Thread

It’s actually not possible to terminate a thread if the thread itself doesn’t want to be terminated. This is not entirely obvious, but it’s true. This is why most threaded operations utilize a flag to tell them when to exit prematurely. For example:

-(void)myThreadedOperation
{
    while(continueToRun && ![self allStuffIsDone])
    {
        // do some stuff...
    }
}

In this case, we have a background thread that is doing a series of operations. When all the operations are done, it sets the returns true from -allStuffIsDone and exits the loop. However, it also has a flag called continueToRun which its checking on each loop. This flag can be set by an external thread to cause this thread to exit after any iteration. If your background operations will take a long time, you probably want to include a similar design pattern.

Myth #9: I can safely throw exceptions in threads.

What happens when an exception is thrown in Objective-C and not caught? Usually, a crash. 1 What happens when an exception is thrown in a background thread and not caught? The answer is, the one thing worse than a crash: undefined behavior. It might crash… It might crash the whole app… It might crash just the thread… No one knows! It’s undefined. Your thread might even crash, and your app continue running. What do you think your app will do if it expected that thread to later either still be running or have finished?

Although I wouldn’t say that you can’t throw exceptions in threads at all, the important thing to know is that if you do, and it doesn’t get caught, the end result for your app is very bad.

Fortunately, Objective-C doesn’t use a lot of exceptions. So I don’t advocate putting generic exception handlers at the top of all your threaded operations. I point this out more as a “thing to know”. That exceptions can be dangerous in threads, and to be careful when working with code that does throw exceptions.

Myth #10: I can update my gui from background threads.

This one is fairly well known, but worth mentioning anyway. Cocoa and CocoaTouch, the GUI frameworks used on Mac OS and iOS respectively, are not thread safe. You must never update your GUI from a background thread. Whenever you need to trigger a GUI update from a background thread, you should do so on the main thread. Here’s an example of how to do this using the main queue and a block:

[[NSOperationQueue mainQueue] addOperationWithBlock:^
    {
        [self.tableView reloadData];
    }];

Myth #11: Notifications from background threads are executed on the main thread.

Notifications are nothing more than a set of method calls. So when a notification occurs, your notification handler will be called from the thread upon which the notification was triggered. This has some interesting implications.

For example, when you’re saving Core Data objects in a background thread, this notification will be sent from that background thread. Ordinarily, this is a great notification to watch for to update your UI when changes occur. Of course, we already talked about how you can’t update your UI from a background thread, but in addition to this, most of Core Data itself is also not thread safe2. In this case, the objects sent with the notification using the NSInsertedObjectsNSUpdatedObjects, andNSDeletedObjects keys in the userInfo dictionary cannot cross threads, so you can’t use them directly, even if you forward the notification to the main thread.

To handle this problem, you need to use the NSManagedObjectContext method, -mergeChangesFromDidSaveNotification: in the thread of the context which is receiving the notification so it can merge the changes. To do this, you can use themainQueue again like I showed you before:

[[NSOperationQueue mainQueue] addOperationWithBlock:^
    {
        [self.ctx mergeChangesFromContextDidSaveNotificaton:
                                     inNotification];
    }];

Myth #12: I shouldn’t use threads

Given all these hazards, you might think that you should never use threads. This is not the case. Threads are a useful tool, but like any tool, they have to be respected.

Threads are like the tablesaw of the Objective-C wood shop. They cause more application injuries than all other technologies combined. There’s times, however, when they’re really the best tool for the job! The important things to remember when using threads are:

  • Keep your thread’s data isolated.
  • Simplify the algorithms you intend to use in threads. Make them the “simplest thing that can possibly work.”
  • Learn the API used for insuring thread safety like it’s the back of your hand. Things like NSLock, @synchronized, NSRecursiveLock, etc.

  1. You can install an unhanded exception handler to change this behavior. 
  2. NSManagedObjectContext for example, can’t cross threads either. This is probably worthy of a post all of it’s own someday! 

Reference : Go Here

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s