Cocoa Worker Thread

For Kompressor.app, I’ve written a group of Foundation classes for implementing a worker-thread paradigm in Objective-C. The general idea is you have some non-trivial amount of processing you want done and then (optionally) be notified when it’s done.

This implementation has a number of advantages compared to others:

  • Threads are created once and reused, no expensive thread creation for each work unit.
  • Non-polling (using NSConditionLock).
  • Low communication overhead (e.g. no Distributed Objects).
  • Small (although the code is spread out over 3 classes / files).

How do you use it?

  1. Create an instance of SIWorkManager (usually there should only be one); by default it creates as many worker threads as CPU cores are available.
  2. For each unit of work, create an SIWorkUnit object (which contains the target object and selector where the actual computation is done, as well as an optional didEndSelector which is called on the main-thread of your application with either the result of calling [target selector] or the originally given argument). The target and argument are retained by the work unit.
  3. Tell the work manager object about the work unit (via addWorkUnit:).
  4. Done!

If you want to wait for a batch of N work units to be done, create an NSConditionLock L, and at the end of the work selector, you do [L lock]; [L unlockWithCondition:[L condition] + 1];. After dispatching all the work units in the main thread (or wherever), you wait with [L lockWhenCondition:N]; [L unlockWithCondition:0];.

An (untested as I am only on a Linux machine at the moment) example demonstrating both simple usage as well as waiting for a batch to be finished is in BatchExample.m.

Remember to only access shared data structures (or your user-interface) in the didEndSelector. SIWorkManager also has messages for removing work on or referring to a given object. Use nil to remove all pending work units.

Here is the source code with all required files. Feedback / fixes appreciated.

6 thoughts on “Cocoa Worker Thread

  1. [maven] Post author

    Updated the post (and the archive) with an (untested) example showing the usage of both individual work units and waiting for a batch of them to be completed.

  2. Kikoo

    Hello,
    Good job ! Something I’ve noticed, in the workLoop: function, you should add “[NSThread exit]” at the end. Otherwise the thread will never die.
    What is the “outerPool” for ?

  3. [maven] Post author

    Thanks.
    The [NSThread exit] is not necessary, as the documentation for [NSThread detachNewThreadSelector:toTarget:withObject:] states “The detached thread is exited (using the exit class method) as soon as aTarget has completed executing the aSelector method.”
    As for the outerPool, strictly it shouldn’t be necessary but I left it in there as I had restructured the loop a few times and this was an easy way to ensure that an autorelease pool is always in scope. As bring-up and tear-down only happens on startup- and quit, it shouldn’t impact performance.

  4. Kikoo

    Ok thanks.
    Something you may find interesting, is NSApplication’s “detachDrawingThread:toTarget:withObject” function. It’s the same as deatchNewThread… but this one creates and destroys a pool automatically.

    I have built a similar class myself, but instead of taking selectors it takes NSInvocations which are very powerful. It would be easy to modify your classes to make them accept NSInvocations (let me know if you are interested / need help).
    And instead of asking sysctl for the number of CPU (ie, physical units), mine asks the number of logical units (ie cores) (but apparently those numbers are the same on a coreduo computer).

  5. [maven] Post author

    I wanted the code to work fine with Foundation, so relying on NSApplication is not something I would want to do (but thanks for the hint, I didn’t know about this convenience method).
    Also, my code asks for the number of cores as well (tested on my Mac Pro ;)), so I don’t know why you think this is not the case.
    Lastly, thanks for the offer of helping with NSInvocation, I looked at them initially but found them a bit too complicated to be easily usable. Secondly, I’ll just wait for Leopard’s NSOperation instead… 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *