Nifty Box News

Wednesday, December 20, 2006

Worker Thread - NSInvocation Part 2

NSInvocation is not only nice to post delayed multi-argument messages, you can also use it for constructing a lightweight worker thread implementation.

Sometimes it happens that your app has to do lot of work, that you would like to do it in the background as you think it would be a nice thing if the gui would stay responsive to the user actions. Sometimes invoking a NSThread is a valid solution, but if you want to schedule many tiny tasks to the background creating a new thread for each of them might not be the best idea.

Another possibility is using Distributed Objects (DO). But DO for intra-application communication might be a bit of an overkill and during my experimenting I experienced, that outsourcing a working thread via DO and using the NSRunLoop is not the ideal solution: if the runloop of the worker thread is currently busy working on a past message you have to wait on the main thread if you want to post a new DO message until the worker thread's runloop is free again.

So a solution I am using for the background Spotlight export of my app is based on the producers and consumer model: I create a thread safe queue in which the producer (main) thread posts new tasks and a consumer thread performs these tasks. This is very lightweight and the main thread does not block during the posting of the tasks.

How does it work? Again using NSInvocations. The tasks, that are written to the queue are just NSInvocations, so you can post any message the target object understands.

So here's an example:

In the main thread you initialize NBInvocationQueue and start the worker thread


NBInvocationQueue *finderEventQueue = [[NBInvocationQueue alloc] init];
[NSThread detachNewThreadSelector:@selector(runQueueThread) toTarget:finderEventQueue withObject:nil];


Now the NBInvocationQueue is ready to take messages. You post them like this:


[[finderEventQueue performThreadedWithTarget:spotlightExporter]
exportFinderCommentAtPath:path
tags:tagArray
comment:comment]

This will invoke [spotlightExporter exportFinderCommentAtPath:...] on the worker thread.

The methods to queue messages are:

-(id)performThreadedWithTarget:(id)target;
-(id)performThreadedWithTarget:(id)target afterDelay:(NSTimeInterval)delay;


If you want to stop the worker thread you can use the method:

-(void)stopThreadWaitUntil:(BOOL)wait;

which waits until all open tasks have been de-queued if the flag "wait" is set.

The source code is here:

NBInvocationQueue.h and NBInvocationQueue.m

Feel free to use this class as you like, NO WARRANTY!

Inspiration for this class has been producers and consumer model and CInvocationGrabber from ToxicSoftware.

Labels: , ,

3 Comments:

  • How about Thread Worker?

    http://iharder.sourceforge.net/macosx/threadworker/

    By Anonymous Anonymous, At 12/21/2006 10:16 AM  

  • The ThreadWorker class produces a new thread for each task. My class just runs one worker thread at a time and the tasks are processed sequentially from the queue. This is nice if you have many tiny tasks like updating 500 files in the background where you can't effort to create 500 real os-threads.

    By Blogger Tim Scheffler, At 12/21/2006 12:22 PM  

  • That's really nice Tim. I wish I had thought of it :-)

    By Anonymous Jonathan Wight, At 12/21/2006 3:42 PM  

Post a Comment



<< Home