Trying to use a dispatch_semaphore
. It should look something like this:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[object runSomeLongOperationAndDo:^{
STAssert…
dispatch_semaphore_signal(sema);
}];
if (![NSThread isMainThread]) {
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
} else {
while (dispatch_semaphore_wait(sema, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
}
This should behave correctly even if runSomeLongOperationAndDo:
decides that the operation isn't actually long enough to merit threading and runs synchronously instead.
Because your device only has one processor, GCD probably only creates one thread for executing blocks and your blocks execute sequentially. You've created 10 different threads, though, and those each get a little piece of the available processing time. Fortunately, sleeping isn't very processor-intensive, so all your threads run together pretty well. Try a similar test on a machine with 4 or 8 processing cores, and you'll see GCD run more of your blocks in parallel.
The nice thing about GCD isn't that it necessarily offers better performance than threads, it's that the programmer doesn't have to think about creating threads or matching the number of threads to the number of available processors. You can create lots of little tasks that will execute as a processor becomes available and let the system schedule those tasks for you.
Edit: I played around with your code a bit in a simple command-line program on my Mac. As I suggested in my comment below, and also mentioned in @Ren-D's answer, using dispatch_async()
rather than dispatch_apply()
makes a big difference. Here's the code I used:
- (void)doIt:(NSNumber *)i
{
for (int j = 0; j < MAX_COUNT; j++)
;
NSLog(@"Thread#%i", [i intValue]);
}
- (void)doWork:(id)sender
{
for (int i = 0; i<10; i++) {
NSNumber *t = [NSNumber numberWithInt:i];
[NSThread detachNewThreadSelector:@selector(doIt:) toTarget:self withObject:t];
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (size_t i = 0; i<10; i++) {
dispatch_async(queue, ^(void) {
for (int j = 0; j < MAX_COUNT; j++)
;
NSLog(@"GCD#%u",(int)i);
});
}
NSLog(@"Done.");
sleep(15);
}
As you can see, I replaced your sleep()
calls with for
loops that spend some time counting. (I ran the code on a MacBook Pro -- you might want to adjust the value of MAX_COUNT
downward if you're running on an iPhone.) If you use sleep()
in both the threads and the block, then dispatch_async()
makes the blocks behave just like the threads -- all blocks run concurrently and complete at about the same time. Switching to counting changes that behavior -- multiple threads all run concurrently, but the blocks execute in groups (my machine has two processor cores, so it ran the blocks in groups of two). This is exactly as you'd expect; GCD's job is to queue tasks and finish them as quickly as possible making the best use of available resources, not to run as many tasks concurrently as possible.
Here's output from the code above:
2011-04-14 02:48:46.840 BlockTest[14969:903] Hello, World!
2011-04-14 02:48:47.104 BlockTest[14969:903] Done.
2011-04-14 02:48:52.834 BlockTest[14969:1503] Thread#0
2011-04-14 02:48:52.941 BlockTest[14969:4f03] GCD#0
2011-04-14 02:48:52.952 BlockTest[14969:5003] GCD#1
2011-04-14 02:48:52.956 BlockTest[14969:4703] Thread#8
2011-04-14 02:48:53.030 BlockTest[14969:3703] Thread#4
2011-04-14 02:48:53.074 BlockTest[14969:2b03] Thread#1
2011-04-14 02:48:53.056 BlockTest[14969:4b03] Thread#9
2011-04-14 02:48:53.065 BlockTest[14969:3b03] Thread#5
2011-04-14 02:48:53.114 BlockTest[14969:3303] Thread#3
2011-04-14 02:48:53.138 BlockTest[14969:4303] Thread#7
2011-04-14 02:48:53.147 BlockTest[14969:3f03] Thread#6
2011-04-14 02:48:53.156 BlockTest[14969:2f03] Thread#2
2011-04-14 02:48:53.909 BlockTest[14969:4f03] GCD#2
2011-04-14 02:48:53.915 BlockTest[14969:5003] GCD#3
2011-04-14 02:48:54.700 BlockTest[14969:4f03] GCD#4
2011-04-14 02:48:54.721 BlockTest[14969:5003] GCD#5
2011-04-14 02:48:55.508 BlockTest[14969:4f03] GCD#6
2011-04-14 02:48:55.550 BlockTest[14969:5003] GCD#7
2011-04-14 02:48:56.321 BlockTest[14969:4f03] GCD#8
2011-04-14 02:48:56.345 BlockTest[14969:5003] GCD#9
Note that two of the blocks actually finished before all but one of the threads. Also: the sleep(15)
at the end of the code is just there to let the threads and blocks log their messages before the program terminates. Depending on what sort of program you paste the code into, you may not need it.
Best Answer
The
dispatch_sync()
trick will only work for serial queues, which is what that tutorial is showing. Thedispatch_get_global_queue()
returns a concurrent queue, see it's documentation note:To deal with a global concurrent queue you should use a group where you submit your blocks, also mentioned on that tutorial, and wait for the whole group with
dispatch_group_wait()
.