I've used both GCD and performSelectorOnMainThread:waitUntilDone in my apps, and tend to think of them as interchangeable--that is, performSelectorOnMainThread:waitUntilDone is an Obj-C wrapper to the GCD C syntax. I've been thinking of these two commands as equivalent:
dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });
[self performSelectorOnMainThread:#selector(doit:) withObject:YES waitUntilDone:YES];
Am I incorrect? That is, is there a difference of the performSelector* commands versus the GCD ones? I've read a lot of documentation on them, but have yet to see a definitive answer.
As Jacob points out, while they may appear the same, they are different things. In fact, there's a significant difference in the way that they handle sending actions to the main thread if you're already running on the main thread.
I ran into this recently, where I had a common method that sometimes was run from something on the main thread, sometimes not. In order to protect certain UI updates, I had been using -performSelectorOnMainThread: for them with no problems.
When I switched over to using dispatch_sync on the main queue, the application would deadlock whenever this method was run on the main queue. Reading the documentation on dispatch_sync, we see:
Calling this function and targeting
the current queue results in deadlock.
where for -performSelectorOnMainThread: we see
wait
A Boolean that specifies whether the
current thread blocks until after the
specified selector is performed on the
receiver on the main thread. Specify
YES to block this thread; otherwise,
specify NO to have this method return
immediately.
If the current thread is also the main
thread, and you specify YES for this
parameter, the message is delivered
and processed immediately.
I still prefer the elegance of GCD, the better compile-time checking it provides, and its greater flexibility regarding arguments, etc., so I made this little helper function to prevent deadlocks:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
Update: In response to Dave Dribin pointing out the caveats section ondispatch_get_current_queue(), I've changed to using [NSThread isMainThread] in the above code.
I then use
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
to perform the actions I need to secure on the main thread, without worrying about what thread the original method was executed on.
performSelectorOnMainThread: does not use GCD to send messages to objects on the main thread.
Here's how the documentation says the method is implemented:
- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}
And on performSelector:target:withObject:order:modes:, the documentation states:
This method sets up a timer to perform the aSelector message on the current thread’s run loop at the start of the next run loop iteration. The timer is configured to run in the modes specified by the modes parameter. When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in one of the specified modes; otherwise, the timer waits until the run loop is in one of those modes.
GCD's way is suppose to be more efficient and easier to handle and is only available in iOS4 onwards whereas performSelector is supported in the older and newer iOS.
Related
I have the following Method:
-(void) waitForStatusChangeAndPerformBlock:(MyBlockType)successBlock;
This is what the method should do:
Check if some status has the right value
If it does invoke the block successBlock
If not wait for the status to change to a given value and then invoke the block successBlock
I thought about KVO to check if the value has changed, but then I would have to store the block in some instance variable, or worse, an array, and I would loose the context of the current method call. So what I really want is something like this:
-(void) waitForStatusChangeAndPerformBlock:(MyBlockType)successBlock{
if(self.status == kDesiredStatus){
successBlock;
} else {
[TheMagicDispatchWaitUntil:(self.status == kDesiredStatus) andThenDoThis:^{
successBlock;
}];
}
}
Is there a way to achieve this without KVO or other helper methods?
If you want a theead to wait on an event - a message, timer, or whatever, one really nice way to do that is to use a Concurrent NSOperation. Those objects run on a separate thread, but have a runLoop so they can block in a the "normal" fashion inside the runloop callback waiting for something to happen.
That said, these do take a bit of finesse to get working. I have a demo project on gthub that lets you explore concurrent NSOperations (and there are others too).
Another nice way to block until something has done (on a thread) is to use "dispatch_wait()", which waits on all blocks that have been queued belonging to a group. This technique is pretty easy to pick up - you create a dispatch group and use the standard queues or create your own queue, then queue blocks using the dispatch_group functions. Once all are queued, you then dispatch_wait(forever) for the blocks to finish.
If you are doing just a simple routine and you don't have to call this method often, why don't you just use a while statement?
while (self.status != kDesiredStatus);
do {TheMagicDispatch}
succesBlock;
In reviewing my code, I've been seeing that in many places I have been making the assumption that calling [NSBlockOperationInstance start]; will start this operation on the main thread. I don't know why I thought this, but I shouldn't have been so sure any way. I checked the documentation but couldn't find any explicit mention of the thread the block would run on. However, asserting assert([NSThread isMainThread]); in the main body of the block does pass every time using start, so I'm not sure if this is a coincidence. Any one have more solid understanding of how this would work?
I forgot to mention that [op start] is being called on the main thread.
OK, it all depends on where you call start(). While NSBlockOperation will farm out blocks to other threads, start() is synchronous, and will not return until all the blocks that have been given to NSBlockOperation have completed.
While NSBlockOperation will concurrently execute the blocks it is given, NSBlockOperation itself is NOT concurrent (i.e., isConcurrent is false). Thus, according to the documentation, start() will execute in its entirety in the thread of the caller to start().
Since the thread that calls start() will not return until all the blocks have executed, it makes sense to let the calling thread be involved in the thread pool that is executing the concurrent blocks. That is why you will see some blocks executing in the thread that called start().
If you are seeing a block execute in the main thread, then you must have called it from the main thread.
On a related note, if your NSBlockOperation contains a single block, than that block will always execute in the calling thread.
Remember, if you want a NSOperation to be fully concurrent, you must implement the appropriate functionality in a subclass.
Barring that, you can give any NSOperation to a NSOperationQueue, and it will execute concurrently, because the NSOperation is given to a queue, and the thread running the operation calls start().
Personally, I do not see any advantage in using NSBlockOperation over dispatch_async() unless I need to use its features. If you are only executing one block, just call
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ });
If you want to utilize the features of NSBlockOperation, but you do not want to wait for them to complete in the current calling thread, it still makes sense to do this...
// Add lots of concurrent blocks
[op addExecutionBlock:^{ /*whatever*/ }];
// Execute the blocks asynchronously
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[op start];
// Now do what you want after all the concurrent blocks have completed...
// Maybe even tell the UI
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI now that all my concurrent blocks have finished.
});
});
EDIT
To address your comment to tc's answer...
If you call
op = [NSBlockOperation blockOperationWithBlock:^{assert([NSThread isMainThread])}];
[op start];
from the main thread, then there are some guarantees, and some high probabilities.
First, you are guaranteed that [op start] will run to completion in the calling thread. That's because NSBlockOperation does not override the default behavior of NSOperation that specifies it is NOT a concurrent operation.
Next, you have a very high probability that if the NSBlockOperation only has one block, that it will run in the calling thread. You have almost the same probability that the first block will run in the calling thread.
However, the above "probabilities" are not guarantees (only because the documentation does not say it). I guess, some engineer may find some reason to spin that single block to one of the concurrent queues, and just have the calling thread join on the operation that is executing in another thread... but I highly doubt that.
Anyway, maybe your confusion comes from the fact that the documentation for NSBlockOperation says it executes block concurrently, which it does. However, the operation itself is not concurrent, so the initial operation is synchronous. It will wait for all blocks to execute, and it may (or may not) execute some of them on the calling thread.
While there is no guarantee, I find it highly unlikely that a NSBlockOperation with only one block will do anything other than execute on the calling thread.
The docs specifically say
Blocks added to a block operation are dispatched with default priority to an appropriate work queue. The blocks themselves should not make any assumptions about the configuration of their execution environment.
I suspect that the following will crash:
NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^{ sleep(1); }];
[op addExecutionBlock:^{assert([NSThread isMainThread]); }];
[op start];
What's wrong with simply executing the block?
I have a low-priority PDF-drawing NSOperation. I am using the function CGContextDrawPDFPage to draw the page.
As best I can tell, the app will not switch to another thread while the CGContextDrawPDFPage function is executing. I suppose I need to break up my graphics context into several smaller contexts to get around that. But I still have one problem -- how can I persuade the operation to allow other threads to perform their tasks before starting that draw? The way things are set up right now, the app draws the page right away, even though the thread priority is set to zero.
Here is the code that adds the NSOperation to the queue:
MyNSOperationSubclass* op = [[MyNSOperationSubclass alloc]initWithStuff: stuff];
[op setThreadPriority:0.];
[self.drawingQueue addOperation:op];
Here is some code within MyNSOperationSubclass:
-(void) drawStuff {
NSLog(#"drawStuff");
#autoreleasepool {
// some code to setup the graphics context and PDF page here
CGContextDrawPDFPage(context, page);
[self finish]; // finishes the NSOperation
}
}
-(void) main {
NSLog(#"main");
#autoreleasepool {
[self drawStuff];
}
}
-(void) start {
self.isExecuting = YES;
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
}
what happens is that even though the thread priority is set to zero, the setup code is so quick that the NSOperation gets into the CGContextDrawPDFPage call right as the operation is started. But once that call has started, the thread won't yield to other threads until it has finished. So the drawing operation happens right away, which is the opposite of what I want to do.
EDIT: After taking a closer look, the first answerer is correct that the CGContextDrawPDFPage call does not always prevent a thread from yielding. But even with a non-concurrent operation, I still have the problem that it finishes quickly, rather than waiting around for other, higher-priority stuff to happen.
EDIT 2: Apparently I don't understand how thread priorities work. After changing it to a nonconcurrent operation, the thread priority of the operation thread is 0.5, even though I called [op setThreadPriority: 0] before adding it to the queue.
So, the first thought is that your thread can't avoid "yielding" to other threads. Context switching is pre-emptive here. So you might want to talk about why you think this thread is not yielding. You may have some other bigger issue here with the other work that you are expecting to be done.
Second, when you set the thread priority for this operation, you're not setting the priority of this new thread that you are creating yourself here:
[NSThread detachNewThreadSelector:#selector(main) toTarget:self withObject:nil];
That's a new thread with normal priority.
But stepping back from that a second, why not just make this a non-concurrent operation and let your NSOperationQueue manage the thread for you? The queue will create a new thread for you as needed (and apply your thread priority that you asked for).
Alternatively, unless you still need to support iOS3, you can just use the global low priority GCD queue for this work (or target your own GCD queue to that global queue if you need finer control over order and still want all of this done with low priority).
So, consider letting either NSOperationQueue or GCD manage some of this for you. And think about why you think this thread is interfering with other work. That's probably some bigger issue.
Hope that helps.
I am trying to learn more about dispatch queues. If I put three methods in a dispatch queue as in the code below, do they execute one after the other or all at once ?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^ {
[activeModel release];
[mainViewController showSceneList];
[mainViewController removeTidyUpScreen];
});
How would I specify that the next should not run until the previous one is completed ?
Think of a block -- the code you submit to a dispatch queue as you have here -- as an anonymous function. So, the code you have in your block here executes in order just as if you were calling a function that contained the same calls, one method, then the next, and so on.
In your particular example, it looks like you may be doing some operations with the UI on a queue that is not the main queue. You MUST do UI operations on the main queue, because it has access to the UI. You might use dispatch_get_main_queue() instead, to be sure you're getting that queue. If you have something you want to run in the background that will not touch the UI, then using a global queue is fine, and preferred especially if not stalling the UI is important.
I have recently become thread curious on iOS. Please point me in the direction you would take, to achieve (if possible) the following on modern iOS devices... thank you!
The user is typing in text, say a word every few seconds.
From time to time I want to launch DifficultProcess to do some semantic processing. In short, I guess I need to be able to do four things:
launch DifficultProcess from main
if DifficultProcess completes, get a message back from it to the same main
abandon, get rid of, DifficultProcess if I want to, from main
and finally the priority question: DifficultProcess must have much lower priority than main or user input, I want DifficultProcess to have really really looow priority; is that even possible?
What, essentially, are the calls one uses for A, B, C in modern (2011) (late January) iOS? I don't care about Dad's methods! And is "D" even possible in any way?
I guess those are the four ideas!
So in particular I want to send a message to, in other words call a routine in, the running background process (in that way, one could kill off the running background process if desired, or perhaps change it's mode of operation etc).
(For anyone born before 1997, you will recognise that as a typical "speculative processing" paradigm.)
Thanks for pointers for anyone who can be bothered on this!
I would recommend using NSOperation and NSOperationQueue to manage background activity that you need to be able to cancel arbitrarily.
NSOperation's -cancel and NSOperationQueue's -cancelAllOperations are the methods to look at.
To get messages back from the background to the main thread, the dispatch_async-to-main-thread-queue technique is fine. You can combine this with a delegate protocol for your NSOperation to codify the messages you want to send back.
E.g.
#protocol MyOperationDelegate
- (void) operationStarted:(MyOperation *)operation;
- (void) makingProgressOnItem:(id)anItem otherInterestingItem:(NSDictionary *)otherItem remainingCount:(NSUInteger)count;
- (void) operationWillFinish:(MyOperation *)operation;
#end
#interface MyOperation
id <MyOperationDelegate> delegate;
#end
#implementation MyOperation
...
- (void) cancel
{
[super cancel];
// Tell the delegate we're about to finish (due to cancellation).
dispatch_sync (dispatch_get_main_queue(), ^{
[self.delegate operationWillFinish:self];
});
}
- (void) main
{
// Check for cancellation
if (self.isCancelled) return;
// Starting
dispatch_sync (dispatch_get_main_queue(), ^{
[self.delegate operationStarted:self];
});
if (self.isCancelled) return; // Another cancel check
// Send async progress messages periodically while doing some work
while (workNotDone)
{
// Do some work ...
dispatch_async (dispatch_get_main_queue(), ^{
[self.delegate makingProgressOnItem:foo otherInterestingItem:bar remainingCount:baz];
});
if (self.isCancelled) return;
}
// About to finish
if (!self.isCancelled) {
dispatch_sync (dispatch_get_main_queue(), ^{
[self.delegate operationWillFinish:self];
});
}
}
#end
KVO is no good for interthread communication; the observation is received on the thread that originates the key value change. So, if your background thread changes a value, your background thread is going to receive the KVO about it. Probably not what you want.
Grandpa's -performSelectorOnMainThread:withObject:waitUntilDone: continues to be a fine way to get messages back to the main thread. The limitation is that your messages can only access one object-based argument. The dispatch_async to the main thread doesn't have this limitation.
If you want to fire off an asynchronous (or synchronous) NSNotification's from a background thread to the main thread, you need to use -performSelectorOnMainThread.
NSNotification *note = [NSNotification notificationWithName:FinishedABunchOfWorkNotification object:self userInfo:nil];
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:#selector(postNotification:) withObject:note waitUntilDone:YES];
I would suggest using dispatch_async to the global low priority queue (dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)).
Cancellation is trickier though. There's no good general mechanism for canceling background work that I'm aware of aside from "chunking" it and checking a flag each chunk
To get messages back just dispatch_async back to the main queue. If you squint just right you can think of dispatch_async as "send message" in an actor model.
(edit) if you need serialization of stuff in the background, make a private queue and set its target to the global low priority one, iirc.
At the risk of quoting Dad's method (it has been around since iPhone version 2) I use
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
It's easy and foolproof as long as you remember that you must create a new autorelease pool in the method you pass as selector, and drain it at the end of the method. Apart from that do whatever you like - EXCEPT touch UIKit. It isn't thread-safe so any UI changes must be done through
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
or KVO triggers. Key Value Observing would be a good way for your background thread to communicate to your main thread that the work is done.
- (void)myBackgroundThreadMethod {
NSAutoreleasePool *threadPool = [[NSAutoreleasePool alloc] init];
// my time-consuming processing here
[threadPool drain];
}
For more precise control of threads you need to look at NSThread. Threading Programming Guide lays it all out in detail - if you create a thread through NSThread then you have control over when the thread is started. The document does recommend leaving the thread alone and just letting it terminate - but shows how you can terminate it. One way is - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait
NSThread docs also say "leave priority alone". You can set thread priority with
+ (BOOL)setThreadPriority:(double)priority
but I've never known it to be necessary, the scheduler is smart enough to maintain UI responsiveness.