Grand Central Dispatch vs. NSThread

20,556

Solution 1

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.

Solution 2

Try to look at this website: http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html

In IOS environment, It is said that dispatch_apply will depend on the queue passed in, if the target queue is a concurrent queue returned by dispatch_get_global_queue (which is your case), the block can be invoked concurrently.

So, I think it IS working, just so it happens that it is run as if it runs asynchronously. Also, which device the code is run at could play a role on the result (like mentioned by @Caleb). But my suggestion is, maybe try dispatch_async instead?

Solution 3

If anyone want to test, which method is the best for the spedify probleme, here ist the code:

#define MAX_COUNT 99999999
#define HOW_MUCH 10
- (void)doIt:(NSNumber *)i
{
    for (int j = 0; j < MAX_COUNT; j++)
        ;
    NSLog(@"Thread#%i", [i intValue]);
}


- (IBAction)doWork:(id)sender
{
    NSLog(@"START");

    for (int i = 0; i < HOW_MUCH; i++) {
        NSNumber *t = [NSNumber numberWithInt:i];
        [NSThread detachNewThreadSelector:@selector(doIt:) toTarget:self withObject:t];
    }

    sleep(3);


    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(HOW_MUCH, queue, ^(size_t i) {
        for (int j = 0; j < MAX_COUNT; j++)
            ;
        NSLog(@"GCD APPLY %u",(int)i);
    });


    sleep(3);

    for (size_t k = 0; k < HOW_MUCH; k++) {
        dispatch_async(queue, ^(void) {
            for (int j = 0; j < MAX_COUNT; j++)
                ;
            NSLog(@"GCD ASYNC#%u",(int)k);
        });
    }

    sleep(10);
    NSLog(@"DONE");
}
Share:
20,556

Related videos on Youtube

CarlJ
Author by

CarlJ

i write 90% ObjC, 10% Ruby code for 'Deutsche Telekom'

Updated on February 24, 2020

Comments

  • CarlJ
    CarlJ about 4 years

    I created some test code for NSThread and Grand Central Dispatch (GCD):

    - (void)doIt:(NSNumber *)i
    {
     sleep(1);
     NSLog(@"Thread#%i", [i intValue]);
    }
    
    - (IBAction)doWork:(id)sender
    {
    
     for (int i = 0; 10 > i; i++) {
        NSNumber *t = [NSNumber numberWithInt:i];
        [NSThread detachNewThreadSelector:@selector(doIt:) toTarget:self withObject:t];
     }
    
     sleep(1);
    
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     dispatch_apply(10, queue, ^(size_t i) {
        sleep(1);
        NSLog(@"GCD#%u",(int)i);
     });
    }
    

    And the results:

    2011-04-13 19:41:07.806 GDC[1494:5e03] Thread#0
    2011-04-13 19:41:07.813 GDC[1494:6903] Thread#3
    2011-04-13 19:41:07.812 GDC[1494:6403] Thread#2
    2011-04-13 19:41:07.812 GDC[1494:5f03] Thread#1
    2011-04-13 19:41:07.813 GDC[1494:6e03] Thread#4
    2011-04-13 19:41:07.814 GDC[1494:7303] Thread#5
    2011-04-13 19:41:07.814 GDC[1494:7803] Thread#6
    2011-04-13 19:41:07.815 GDC[1494:7d03] Thread#7
    2011-04-13 19:41:07.815 GDC[1494:8203] Thread#8
    2011-04-13 19:41:07.816 GDC[1494:8703] Thread#9
    2011-04-13 19:41:08.812 GDC[1494:707] GCD#0
    2011-04-13 19:41:09.816 GDC[1494:707] GCD#1
    2011-04-13 19:41:10.819 GDC[1494:707] GCD#2
    2011-04-13 19:41:11.825 GDC[1494:707] GCD#3
    2011-04-13 19:41:12.828 GDC[1494:707] GCD#4
    2011-04-13 19:41:13.833 GDC[1494:707] GCD#5
    2011-04-13 19:41:14.838 GDC[1494:707] GCD#6
    2011-04-13 19:41:15.848 GDC[1494:707] GCD#7
    2011-04-13 19:41:16.853 GDC[1494:707] GCD#8
    2011-04-13 19:41:17.857 GDC[1494:707] GCD#9
    

    NSThreads work as I expected: The tasks run concurrently and each thread sleeps for 1 second.

    The dispatch_apply does not work as I expected: Why is the order sequential? Why does each loop wait until the previous loop finishes?

    Thanks for the help.

  • CarlJ
    CarlJ about 13 years
    So on an iOS Device GCD is useless? And i should use NSThreads?
  • Caleb
    Caleb about 13 years
    Not at all. Try dispatch_async() instead.
  • CarlJ
    CarlJ about 13 years
    big thx for your answer. So my conclusion is that dispatch_apply isnt very helpful on the mac os x or iOS. After all i use dispatch_async but i have to handle the callback, if the complete queue is finsihed, by my self. And as you can see the complete Thread Block need 0,5 sec to finish and the GCD Block need more than 4 Seconds to finish. The important things is that you should you use NSThread or GCD by the specifiy problem that you have. Low Profile Task = Threads and other high Profile Task = GCD.
  • Dan Rosenstark
    Dan Rosenstark over 12 years
    +1 fabulous answer, with actual labwork to backup the hunches. Wow.
  • Kaolin Fire
    Kaolin Fire almost 11 years
    That page is for the mac reference, which sadly is different than the (tagged) iOS reference ... "If the target queue is a concurrent queue returned by dispatch_get_global_queue, the block can be invoked concurrently, and it must therefore be reentrant-safe. Using this function with a concurrent queue can be useful as an efficient parallel for loop."
  • Kaolin Fire
    Kaolin Fire almost 11 years
    That page is for the mac reference, which sadly is different than the (tagged) iOS reference ... "If the target queue is a concurrent queue returned by dispatch_get_global_queue, the block can be invoked concurrently, and it must therefore be reentrant-safe. Using this function with a concurrent queue can be useful as an efficient parallel for loop." dispatch_apply is (generally?) async on iOS.
  • Rpranata
    Rpranata almost 11 years
    My bad, the original tag, where I answered this question was cocoa and ios. I made mistake by assuming that it was on cocoa instead.

Related