views:

64

answers:

3

Hi guys,

Let's say I have 2 threads, one is the main thread and another one, a secondary thread. The main thread is being used the most, but sometimes (rarely) I want the secondary thread to do some work based on calls from the main thread. Most of the time the secondary thread should sleep. Now after some searching I understand the way to do this is to use runLoops. So I tried to read apple's docs (http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW5)

but it looks to me very complex and I'm having some hard time there. Is there an elegant and simple way to achieve what I described? Any similar runLoop code examples out there that I can run and play with?

Thanks

+4  A: 

This sounds like just the sort of thing NSOperation/NSOperationQueue was made for. If you only have the occasional "units of work", why not make them an operation, then monitor it for completion and update your UI accordingly?

Joshua Nozzi
Ok I tested your suggestion and it isn't suitable in my situation. The "helper" thread gets a task every 1 second, so in this case it means that every second a new NSOperation is created and added to the queue.
Alex1987
How exactly isn't it suitable? If you're referring to performance, how exactly did you test it? NSOperation is VERY lightweight and the queue can be kept unsuspended (and won't "spin" while it's empty).
Joshua Nozzi
Well, what I'm actually doing is attempting to stream a video on the iphone. The way I do this is basically to use the main thread to download data from a remote server, and use the "helper" thread to read the data and pass it to the player. Right now everything is done with one thread which is kinda ok, but I want to improve it more. So I tested your suggestion and added a new NSOperation for each "read" and got a substantial decrease in performance.
Alex1987
Now we're getting somewhere. :-) Bill's suggestion of Matt Gallagher's article is definitely promising in your case, but maybe posting more details about the type of stream would yield alternatives suggested by people who know better than us. :-) For instance, are you trying to add support for a different codec that's not supported by the platform? If so, which? If not, why not use the built-in support?
Joshua Nozzi
Unfortunately I can't disclose more info about the app :-) Maybe you will see it in the appstore soon! Thanks for your help :)
Alex1987
+2  A: 

Matt Gallagher has a nice blog article comparing the secondary thread approach with other ways of getting background work done.

http://cocoawithlove.com/2010/09/overhead-of-spawning-threads.html

In your case, you don't have to be concerned with thread-creation overhead. But Matt's code examples might provide some insight into managing the secondary thread's runloop.

All that said, I would go with Joshua's advice and just use an NSOperationQueue and an NSOperation to do the background work. If the work could be encapsulated in an NSInvocation, you can use an NSInvocationOperation and avoid an NSOperation subclass.

Bill Garrison
I saw in the code some RunLoops! :) I'll definitely try this out.
Alex1987
+2  A: 

Each thread has a run loop.

Each run loop has a list of things that need to be done. These things are said to be “scheduled” on the run loop, although not all of them are scheduled for a specific date and time:

  • Timers are.
  • Sources aren't. They generally wait for something to come knocking at a Mach kernel port or a file descriptor.

When the run loop is running, it's usually not running—that is, the thread is sleeping, not consuming any CPU cycles. (If you sample it, you'll find the process appearing to be stuck in mach_msg_trap. This is the “wait-for-something-to-happen” system call.) The kernel wakes up the thread (which thereby returns from mach_msg_trap) when something happens that the thread's run loop needs to take care of.

The way to do exactly what you described is to implement a run loop source. You schedule the source on the secondary thread's run loop, implement it by doing work, and signal it from the primary thread when there's work to be done.

However, NSOperation is almost certainly a better solution, as it's designed for the case you described: Discrete units of work that need to be done serially, up to N (which you choose and is at least 1) at a time.

Note that NSOperationQueue reuses threads, so it does not necessarily create a new thread for every operation. Indeed, not doing that is part of the point: It creates the threads lazily, and uses any that it already has that aren't doing anything.

Peter Hosey