I'm having a tiny question on a situation I'm facing.
I have 2 methods :
- (void)firstSelector {
[self launchAsyncTask];
... do some work for a long time (10secs) ...
}
- (void)asyncTaskFinished {
... some work after 5secs of async task ...
}
firstSelector performs launchAsyncTask which is just a background task that has a callback called asyncTaskFinished.
Assuming that firstSelector runs for a certain time after launching the async task (let's say 10 seconds), and that the async task runs for 5 seconds, will there be a concurrency problem?
How does this work under the hood?
Will asyncTaskFinished be executed after firstSelector, or firstSelector will be paused to run asyncTaskFinished?
Is there a link with run loops? Are methods added to a queue and then executed as I call them?
I'm lost :)
Thank you.
Async tasks run asynchronously which do not run on the main run loop while UI runs on the main loop. Have a look at the Concurrency Programming Guide.
So here in your case, you are not really sure about how long the sync time is going to take. You are assuming that it might take 10 sec but it is not completely sure. So, in this case you will need to work with block or find a way to trigger the asyncTaskFinished function when the async task completed on the main thread. You could define a simple block callbacks and then trigger the function when the async task finishes.
If you use GCD for async task, it becomes really easy. You would do just this much;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self runMyAsyncTask];
// trigger the main completion handler when this completed
dispatch_async(dispatch_get_main_queue(), ^{
[self asyncTaskFinished];
});
});
If you use NSThread for concurrency you could use performSelector:onThread: to trigger the completion selector when the async task finishes. For a simple case, I will show you to implement a callback handler. You could create a function like this to trigger the async task,
-(void)launchAsyncTaskWithCompletionHandler:(void(^)(void))completionHandler{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self runMyAsyncTask];
// trigger the main completion handler when this completed
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler();
});
});
}
And calling this is relatively simple;
[self launchAsyncTaskWithCompletionHandler:^{
[self asyncTaskFinished];
}];
This is easy to understand and makes your code much clearer. Hope this helps you.
A thread's run loop executes tasks from its input sources in a sequential (non-concurrent) fashion. Therefore, you do not need to worry as long as firstSelector and asyncTaskFinished are executed in the same run loop. This will depend on the callback semantics. You may need to use something like performSelector:onThread:withObject:waitUntilDone: to ensure the asynchronous callback function is executed in the correct thread.
Related
So I have two methods
-(void)someMethod
{
[self someOtherMethod];
//Do some other stuff
}
-(void)someOtherMethod
{
//Do some other stuff
}
And I was wondering if it is possible to run someOtherMethod as shown in the code above, but without waiting for someOtherMethod to finish running before continuing on.
dispatch_async(... some queue ..., ^{
[self someOtherMethod];
});
Note that you can use one of the global concurrent queues, but you need to be careful to not pound on it or you'll end up with dozens of threads. You may want to create your own concurrent serial queue and enqueue there.
{
dispatch_queue_t myQueue = dispatch_queue_create("com.mycompany.myqueue", 0);
dispatch_sync(myQueue, ^{
//Do EXTREME PROCESSING!!!
for (int i = 0; i< 100; i++) {
[NSThread sleepForTimeInterval:.05];
NSLog(#"%i", i);
}
dispatch_sync(dispatch_get_main_queue(), ^{
[self updateLabelWhenBackgroundDone];
});
});
}
I am getting a deadlock here. According to Apple documentation
"dispatch_sync": "Submits a block to a dispatch queue for synchronous
execution. Unlike dispatch_async, this function does not return until
the block has finished. Calling this function and targeting the
current queue results in deadlock.".
However, I do the outer dispatch_sync on myQueue and then I do inner ditpatch_sync on a different queue which is `main_queue.
Can not find out the reason for the deadlock. Any comments/help are appreciated here.
If you dispatch_sync to myQueue like that and the call happens on the main thread, then dispatch_sync will, if possible, execute the block right there and not on a new worker thread like dispatch_async would. You're not guaranteed to get a separate worker thread for your queue.
The block then runs on the main thread until it hits your second dispatch_sync call, which happens to target the main queue. That queue can't be serviced, since there's already a block running on it, and that's where you end up in a deadlock.
If that's your problem, i.e. the first dispatch_sync is indeed coming from the main thread, then you should switch to dispatch_async. You wouldn't want to block the main thread with the long-running "EXTREME PROCESSING" operation.
You are calling dispatch_sync twice. The first time suspends the main thread waiting for your block to complete. The block then suspends the background thread with the second call which tries to push back to the main thread (which will never process the block from its queue because it's suspended). Both threads are now waiting for each other.
At least one of the calls needs to be dispatch_async.
I had similar problems and none of these solutions worked. I asked someone smarter than me.
My problem was I was spawning a dispatching an async worker block, and then displaying a progress window. Calls back into the main thread via
dispatch_sync(dispatch_get_main_queue(), ^{})
failed as did async calls.
The explanation was that the main thread was no longer in 'commons mode' because of the modal window. I replaced my calls to the main thread with this....
CFRunLoopPerformBlock(([[NSRunLoop mainRunLoop] getCFRunLoop]), (__bridge CFStringRef)NSModalPanelRunLoopMode, ^{
//Update UI thread.
});
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 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'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.