19

我为 NSThread 和 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);
 });
}

结果:

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 像我预期的那样工作:任务并发运行,每个线程休眠 1 秒。

dispatch_apply 没有像我预期的那样工作:为什么顺序是顺序的?为什么每个循环都等到前一个循环完成?

谢谢您的帮助。

4

3 回答 3

34

因为你的设备只有一个处理器,GCD 可能只创建一个线程来执行块并且你的块顺序执行。不过,您已经创建了 10 个不同的线程,并且每个线程都获得了一小部分可用处理时间。幸运的是,睡眠不是非常占用处理器资源,因此您的所有线程都可以很好地一起运行。在具有 4 或 8 个处理核心的机器上尝试类似的测试,您会看到 GCD 并行运行更多块。

GCD 的好处并不在于它必须提供比线程更好的性能,而是程序员不必考虑创建线程或将线程数量与可用处理器的数量相匹配。您可以创建许多将在处理器可用时执行的小任务,并让系统为您安排这些任务。

编辑:我在我的 Mac 上的一个简单的命令行程序中玩弄了你的代码。正如我在下面的评论中所建议的那样,并且在@Ren-D 的回答中也提到过,使用dispatch_async()而不是dispatch_apply()产生很大的不同。这是我使用的代码:

- (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);
}

如您所见,我用花费一些时间计算sleep()的循环替换了您的调用。for(我在 MacBook Pro 上运行代码——如果您在 iPhone 上运行,您可能需要调整 down 的值MAX_COUNT。)如果您sleep()在线程和块中都使用,则dispatch_async()使块的行为就像线程一样-- 所有块同时运行并在大约同一时间完成。切换到计数会改变这种行为——多个线程都同时运行,但块是成组执行的(我的机器有两个处理器内核,所以它以两个为一组运行块)。这正是您所期望的;GCD 的工作是对任务进行排队并尽可能快地完成它们,以充分利用可用资源,而不是尽可能多地同时运行任务。

这是上面代码的输出:

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

请注意,其中两个块实际上在除一个线程之外的所有线程之前完成。另外:sleep(15)代码末尾的 只是为了让线程和块在程序终止之前记录它们的消息。根据您将代码粘贴到哪种程序中,您可能不需要它。

于 2011-04-13T18:20:03.553 回答
7

试着看看这个网站:http: //developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html

在IOS环境下,据说dispatch_apply会依赖传入的队列,如果目标队列是返回的并发队列dispatch_get_global_queue(就是你的情况),block可以被并发调用。

所以,我认为它正在工作,只是碰巧它就像异步运行一样运行。此外,运行代码的设备可能会对结果产生影响(就像@Caleb 提到的那样)。但我的建议是,也许尝试一下dispatch_async

于 2011-04-14T00:14:15.267 回答
2

如果有人想测试,哪种方法最适合加速问题,这里是代码:

#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");
}
于 2011-04-15T16:58:05.237 回答