0

Trying my hand with blocks in Objective C. I ran into a strange problem.

Below i have created a block and submitted the block for asynchronous execution on a global dispatch queue. It doesn't print anything for me. When i replaced the keyword async with sync it works fine and prints the result immediately.

@implementation BlockTest
+(void) blocksTest{
__block int m = 0;
void (^__block myblock)(void);
myblock = ^(){
     NSLog(@"myblock %u ", ++m);
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"dispatch_async dispatch_get_global_queue");
    myblock();
});
}
@end

int main(int argc, const char * argv[])
{
[BlockTest blocksTest];
}

Can someone please help me with this problem ?

2
  • It's probably because the program ends before async block has a chance to be executed. If you try it in a more real app with AppDelegate, and write your dispatch code in application:didFinishLaunchingWithOptions: method, you'll see expected result Commented Apr 11, 2015 at 11:06
  • You are right Maciej this works perfectly fine in application life cycle. But the issue was about plain objective C project. Commented Apr 11, 2015 at 12:45

2 Answers 2

1

Your program is exiting before the block has a chance to run.

The nature of an asynchronous call like dispatch_async() is that it returns and allows the caller to continue on, possibly before the task that was submitted has completed. Probably before it has even been started.

So, +blocksTest returns to the call site in main(). main() continues to its end (by the way, without returning a value, which is bad for a non-void function). When main() returns, the whole process exits. Any queues, tasks, worker threads, etc. that GCD was managing is all torn down during process termination.

The process does not wait for all threads to exit or become idle.

You could solve this by calling dispatch_main() after the call to +blocksTest in main(). In that case, though, the program will never terminate unless you submit a task which calls exit() at some point. For example, you could put the call to exit inside the block you create in +blocksTest.

Actually, in this case, because the task would run on a background thread and not the main thread, anything which delays the immediate exit would be sufficient. For example, a call to sleep() for a second would do. You could also run the main run loop for a period of time. There's no period of time that's guaranteed to be enough that the global queue has had a chance to run your task to completion, but in practical terms, it would just need a fraction of a second.

There's a complication in that methods to run the run loop exit if there are no input sources or timers scheduled. So, you'd have to schedule a bogus source (like an NSPort). As you can tell, this is a kludgy approach if you're not otherwise using the run loop for real stuff.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks Ken ! This is great. I offloaded my function from ViewController to a plain objective C project but didn't realize there is no event loop running here which ll keep the main thread active.
This lead me to try dispatch_sync on main queue which doesn't work. dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"dispatch_sync dispatch_get_main_queue"); myblock(); }); I was expecting this to work, do you know whats wrong with it?
If you use dispatch_sync(dispatch_get_main_queue(), ...) from the main thread, you will always deadlock. dispatch_sync() is, of course, synchronous. It doesn't return until the task has run to completion. Until then, the main thread can't do anything else. It's completely blocked. The main queue tries to run its tasks on the main thread. But the main thread is blocked waiting for the task to complete. The task can't run, let alone complete, until the main thread returns to dispatch_main() or the main run loop. It's a Catch-22 (unsolvable situation).
0

Because when you used dispatch_async, the block may have started the execution, but haven't reached the point where you print. However, if you use dispatch_sync, it makes sure that the entire block execution is done before it returns. Remember, dispatch_sync is the main thread.

1 Comment

dispatch_sync() isn't inherently the main thread. It's just synchronous. It blocks whatever thread it's called from until the task is completed. The task is often run on the thread which called dispatch_sync() since that thread is, by definition, not doing anything else. However, it's actually possible that the task is run on some other thread. The docs say "As an optimization, this function invokes the block on the current thread when possible." (Emphasis added.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.