I have a performance-sensitive code, working with frames of video-playback in real time. I have some work here which can be parallelised and since it's a performance-sensitive code where latency is the key I decided to go with NSThread instead of GCD.
What I need: I need to have NSThread which will be scheduled with some work at a certain period of time. After the thread is done with its work, it goes to sleep till new work arrives.
Unfortunately, there're not so much info about correct techniques of NSThread usage in the internet, so I assembled my routine based on information bits I managed to find.
You can find the whole workflow above:
1) Init my NSThread. This code is launched only once as it's supposed to.
_myThread = [[NSThread alloc] initWithTarget:self selector:@selector(_backgroundMethod) object:nil];
_myThread.threadPriority = 0.8; //max priority is 1.0. Let's try at 0.8 and see how it performs
[_myThread start];
2) _backgroundMethod code:
- (void)_backgroundMethod
{
NSLog(@"Starting the thread...");
[NSTimer scheduledTimerWithTimeInterval:FLT_MAX target:self selector:@selector(doNothing:) userInfo:nil repeats:YES];
BOOL done = false;
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
do {
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!done);
}
- (void)doNothing:(NSTimer *)sender { }
3) When thread has something to work with I make the next call:
[self performSelector:@selector(_doSomeCalculation) onThread:_myThread withObject:nil waitUntilDone:NO];
Which calls next method:
- (void) _doSomeCalculation
{
//do some work here
}
So my question would be:
1) When I init NSThread, I pass a selector. What's the purpose of that selector? As far as I understand, the only purpose of this selector is controlling thread's RunLoop and I should not do any calculations here. So I'm engaging NSRunLoop with an infinite timer just to keep it alive without constant running a while loop. Is that right approach?
2) If I can do a calculation in the selector I'm passing at NSThread init phase - how can I signal NSRunLoop to do one loop those without using performSelector? I think I should not pass the exact same method with performSelector because it would be a mess, right?
I've read a lot of info Apple provided but all of this is almost theoretical and those code samples which provided confused me even more..
Any clarification would be very appreciated. Thanks in advance!
EDIT:
Also a question - how I can calculate a desired stackSize for my thread? Is there any technique to do that?
You should generally not do that unless you have a solid understanding of GCD and know exactly what you're giving up. Used correctly, GCD is highly optimized, and integrated very closely with the OS. It's particularly surprising to be using NSThread by hand, but then doing your work in ObjC with
performSelectorand run loops. CallingperformSelectorthis way is introducing the same kind of unknown latency as a GCD serial queue. If the thread is already busy, then you'll queue the selector, exactly like queuing a block (but you'll add the overhead ofobjc_msgSend). A GCD concurrent queue would perform better. In order to match GCD, you would need to implement a proper thread pool (or at least add cancelation). Done well, this can be better than GCD for specific use cases, but it has to be done very well, and that's complicated.As the Threading Programming Guide notes:
If you want low-latency communication between threads, you'll typically want to use semaphores such as
NSConditionLockrather than a runloop.That said, let's get to the actual questions.
The
performSelector:onThread:...interface is generally used for one-shot operations. The more common way to implement a long-running, dedicated thread is to subclassNSThreadand overridemain. Something like this (this is thrown together and untested, based on code from the Threading Programming Guide; I've done all my high-performance work in GCD for years now, so I probably have goofed something here).You'd spawn up some threads like:
And you'd shut down threads like: