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

デバイスにはプロセッサが 1 つしかないため、GCD はおそらくブロックを実行するためのスレッドを 1 つだけ作成し、ブロックは順次実行されます。ただし、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()同じように動作します。 -- すべてのブロックが同時に実行され、ほぼ同時に完了します。カウントに切り替えると、その動作が変わります。複数のスレッドはすべて同時に実行されますが、ブロックはグループで実行されます (私のマシンには 2 つのプロセッサ コアがあるため、ブロックは 2 つのグループで実行されます)。これはまさにあなたの予想通りです。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

ブロックのうちの 2 つが、1 つを除くすべてのスレッドよりも先に実際に終了したことに注意してください。またsleep(15)、コードの最後の は、プログラムが終了する前にスレッドとブロックがメッセージをログに記録できるようにするためのものです。コードを貼り付けるプログラムの種類によっては、必要ない場合があります。

于 2011-04-13T18:20:03.553 に答える
7

この Web サイトを見てみてください: http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html

dispatch_applyIOS環境では、渡されたキューに依存すると言われていますが、ターゲットキューが返された同時キューdispatch_get_global_queueである場合(あなたの場合)、ブロックを同時に呼び出すことができます。

だから、それは機能していると思いますが、たまたま非同期で実行されているかのように実行されています。また、コードが実行されるデバイスは、結果に影響を与える可能性があります(@Calebで言及されているように)。dispatch_asyncしかし、私の提案は、代わりに試してみてください。

于 2011-04-14T00:14:15.267 に答える
2

誰かがテストしたい場合、どの方法がspedifyの問題に最適か、ここにコードがあります:

#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 に答える