Monday, May 10, 2010

PSA: Respect the Main Thread

I tweeted this earlier as a joke, but it's actually a very serious thing. I've had several jobs in the last year where I've been asked to look at and either suggest fixes or actually fix iPhone applications written by other developers. One thing that I've seen several times in code I've reviewed is something like this littered throughout the application:


This code causes the thread it's called from the do nothing for the specified length of time. Then I'll look through the code for some indication that the application is spawning threads. So far, in every case, the app hadn't spawned any threads, explicitly, or implicitly using NSOperationQueue. If you haven't detached any threads, and you call sleepForTimeInterval:, you are sleeping the application's main thread, and that is a very bad thing to do.

Usually, I see these sleepForTimeInterval: calls in applications that do some kind of asynchronous network communication. My guess is that the developers who wrote these applications came from Java, .Net, or some other language where network communication is commonly handled on a non-main thread. In those languages, it's not uncommon to see code that puts the network worker thread to sleep or into a loop to make it wait for a response and account for any potential lag time. It's not a great approach, but in these environments, it generally works.

In Cocoa and Cocoa Touch, however, unless you specifically spawn a thread and register your network communications with the spawned thread's run loop, your network communications using CFNetwork as well as any networking done using the built-in networking functionality that exists in many Cocoa classes all happens on the main thread.

Another related problem that I sometimes see is something like this:

- (IBAction)soSomething
NSData *data = [NSDatadataWithContentsOfURL:[NSURLurlWithString:@""]];
// parse really big data file and break when done


This code has a few really bad things going on. It's important to keep in mind that IBAction methods always fire on the main thread. So, the first problem is the call to dataWithContentsOfURL:. This is what's called a blocking network operation, which means that calling this method will prevent any further code on this thread from executing until all the data has been received because the method will not return until that has happened. The same thing is true for all of the convenience constructors that contain withContentsOfURL: in their name. While you can probably get away with very limited use of these methods for very small pieces of data, you really should be only using asynchronous network calls in your apps. You can't predict how long network communications will take, even with tiny pieces of data. If the radios are powered down, EDGE connections can take several seconds to re-establish, which is long enough that your user will probably notice the freeze.

The second problem with this thread is the while loop. Small loops are often okay in action methods, but those that could potentially be large or where the length of the loop is simply impossible to know (which is typically the case with while (1) or while(TRUE) loops), you don't want to be doing the processing on the main thread.

Why All This is Bad

If you sleep the main thread, quite literally nothing can happen in your app except for background thread execution. If you haven't spawned any background threads, then pretty much nothing can happen at all. Your user interface will freeze up and your user won't be able to use the application. Buttons won't highlight when tapped and animations will freeze in place. But it's even worse than the interface freezing (which is certainly bad enough), all event processing and network communications will freeze up also. Anything that is handled by your application's main event loop simply doesn't happen while the main thread is asleep.

I have yet to see a situation where calling sleepForTimeInterval: on the main thread is a good idea.

If you perform a long-running operation on the main thread, your code will monopolize the main thread and the end result will be essentially the same as sleeping the main thread while your loop runs. No user interaction, no network communications, no animation.

If you want a good introduction on how to get stuff off your main thread, check out Dave Dribbin's blog post on concurrent operations. More iPhone 3 Development also devotes a full chapter to different types of concurrency.


Jonathan said...

I wonder they didn't use NSOperationQueue?

warmi said...

Well, this is really no different than coding any GUI .. be it Win32, Qt or whatever ... it used to be a major problem in GUI apps... back in 1996 or so :-)

I would think by now people got used to handling this sort of stuff in an asynchronous manner but alas, it looks like that's not the case.

Jeff LaMarche said...


Well, some environment will implicitly create threads for some tasks, so in some cases, this is people applying a technique that works somewhere else.

But, I've come to realize that there are a lot of people doing iPhone development who have never worked with a language where this is an issue. It never occurred to me before to say anything about it, but I've now seen these things done enough in the wild by people who are holding themselves out as professional iPhone developers that it seemed worth a post.

warmi said...

Hehehe ...

Most of the time (from my experience) people know exactly what is happening so it is not simply inexperience but more like laziness - they make assumptions that whatever blocking call they are making won't block for long and thus they will be able to get away with it.
I don't condone it but it certainly simpler and neater to assume linear progress of your code and don't bother with separate handlers and all that stuff.

But you are right ... at least in one case I have seen a guy expecting certain dialogs to behave the way modal dialogs behave on windows - a blocking call which won't come back but which internally creates a new message loop and thus keeps everything else working even though the call itself is very much a blocking method.

Vairn said...

It scares me that people program that way, even on windows It would be wrong.

I guess just learning to program on an amiga, and old Dos machines teaches you to learn about a system, then just making assumptions.

I mean, the documentation says how everything works right? how hard is it to read it?

balexandre said...

Hi Jeff,

Instead of just pointing the bad things, can you update with "What should you do in this cases", mention your point of view in the examples you mention?

At least you not only say it's wrong, but you can give your opinion on a valid solution for those lines you say you often see it in code, most regarding the while(1)...

Not for me, but, can help the basic guy that comes here from a Google Search :)

just my opinion, discharge it if you don't think it's a good idea.

Jeff LaMarche said...


I didn't just point out "the bad thing", I pointed to not one, but two sources of information that tell people how to avoid doing this. I've written threes chapter directly related to the subject (More iPhone 3 Development, chapters on Concurrency, GameKit, and Online Play) and Dave Dribbin does a great job with the subject on the blog posting I linked out to.

As it happens, I do have a blog posting in the works on how to design applications that use asynchronous network communications, but it is complex and will be some time before I can finish it. In the meantime, I thought it was worth putting people on guard about something that is a far more common mistake than it should be.

Sayle said...

From my experience this is usually from .NET people that write WinForms applications. I have had to fix this MANY times in .NET applications for my company by adding network communication on a worker thread or on a newly spawned thread.

I personally think it is from people slopping together code nowadays and not knowing anything about multithreaded programming. Even when a lot of those people start doing multithreaded programming they usually don't realize things like mutexes and semaphores.

I think people doing development in general need to learn sound multithreaded principles before coding, but that's just me.

Joe said...

(Insert shudder here.) I wonder why they simply didn't use NSURLConnection?

Your concurrency chapter (14) nails it, BTW. :)

China wholesale said...

Anyway, the trend of development for iPhones is creative,as things are all gong on its own way to survive,no matter how much difficulties there is.

xiu0519 said...

Happy to share your blog!
good article!I love it
Dell Inspiron 6400 battery
Dell Inspiron 6000 battery
DELL Latitude D620 battery
DELL Latitude D820 Battery
Dell Latitude D600 Battery
Dell Inspiron E1705 battery
Dell Inspiron 1525 battery
DELL XPS M1530 battery
DELL XPS M1330 battery
HP Pavilion Dv9000 Laptop battery
HP Pavilion DV1000, DV1600, DV1700, DV5000, DV5100
HP Pavilion Dv2000, Dv6000, Presario V3000, V6000 Laptop Battery
HP Pavilion zx5000, zv5000, zv6000, zd8000
HP OmniBook 6000 Series
HP Compaq Presario b1200 laptop battery
HP Compaq Business Notebook NC62xx, NC6100, NC6105, NC6200,NC 6
HP COMPAQ Business Notebook 7400, 8200, 8400, 9400, nc, nw, nx S
COMPAQ EVO N800 Battery
COMPAQ Presario 700 Battery
COMPAQ Presario 900 Battery
COMPAQ Presario 1200 Battery
COMPAQ Presario 1700 Battery
Acer Aspire One battery
ACER Ferrari 3200 battery
ACER TravelMate 290 Series battery

Tracy said...

I tried Boost Audience for development of iPhone applications. It’s been a great success. I have hugely benefited with overwhelming results. You must try. They have a very skilled and technically sound team.

SEO Services Consultants said...

Nice information, many thanks to the author. It is incomprehensible to me now, but in general, the usefulness and significance is overwhelming. Thanks again and good luck! Web Design Company

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channels

Devin said...

Thanks for sharing this article on pSA. I am looking for it only.
generic viagra Online