Великий Центральный Диспетчер против NSThread - PullRequest
19 голосов
/ 13 апреля 2011

Я создал тестовый код для 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 не работает, как я ожидал: почему заказ последовательный? Почему каждый цикл ожидает завершения предыдущего цикла?

Спасибо за помощь.

Ответы [ 3 ]

34 голосов
/ 13 апреля 2011

Поскольку ваше устройство имеет только один процессор, 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 - вы можете изменить значение MAX_COUNT вниз, если вы работаете на iPhone.) Если вы используете 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) в конце кода как раз для того, чтобы потоки и блоки могли записывать свои сообщения до завершения программы.В зависимости от того, в какую программу вы вставляете код, он может вам не понадобиться.

7 голосов
/ 14 апреля 2011

Попробуйте посмотреть на этом веб-сайте: http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html

В среде IOS говорят, что dispatch_apply будет зависеть от переданной очереди, если целевая очередь является параллельной очередью, возвращаемой dispatch_get_global_queue (в вашем случае), блок может быть вызван одновременно.

Итак, я думаю, что он работает, просто так получается, что он работает так, как будто он работает асинхронно.Кроме того, какое устройство запускает код, может повлиять на результат (как упомянуто @Caleb).Но я советую попробовать dispatch_async вместо

2 голосов
/ 15 апреля 2011

Если кто-то хочет проверить, какой метод лучше всего подходит для решения проблемы со скоростью, вот код:

#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");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...