Objective-Cでスレッドをデッドロックさせずに非同期処理の終了を待つ方法。

Objective-Cで非同期処理を同期処理にする方法。」に、ディスパッチセマフォによってスレッドがデッドロックしてしまう場合があるということを書きました。

デッドロックする例

これがその例です。

NSLog(@"start.");
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    for (int i = 0; i < 5; i++) {
        sleep(1);
        NSLog(@"Process: %d", i);
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"Main Process");
        });
    }
    dispatch_semaphore_signal(semaphore);
});
NSLog(@"wait...");
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
NSLog(@"finish.");

非同期処理の中で、dispatch_sync()とdispatch_get_main_queue()によって処理をメインスレッドに投げ込んでいます。
具体的にこのような処理が行われるのは、HTTP通信によってデータを取得した後に、UI操作をするためにメインスレッドに処理を戻す場合などです。

2012-12-01 21:21:53.697 AsyncToSync[71197:4703] start.
2012-12-01 21:21:53.700 AsyncToSync[71197:4703] wait...
2012-12-01 21:21:54.701 AsyncToSync[71197:6103] Process: 0

実行すると、このようにdispatch_sync()で処理をメインスレッドに戻そうとしても、ディスパッチセマフォによってロックされているため処理が戻せません。これは永久に抜けられません。

解決策

この問題は、ディスパッチセマフォでメインスレッドをロックするのではなく、ランループに処理を戻すことで対処することができます。
実際にロックされてしまうのは以下の部分です。

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

これを以下のようにすればロックしなくなります。

while(dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];

これは、ディスパッチセマフォで非同期処理の終了を確認し、終了していなかった場合は0.1秒の間ランループに処理を戻しています。

2012-12-01 21:22:28.024 AsyncToSync[71261:4703] start.
2012-12-01 21:22:28.028 AsyncToSync[71261:4703] wait...
2012-12-01 21:22:29.029 AsyncToSync[71261:6103] Process: 0
2012-12-01 21:22:29.031 AsyncToSync[71261:4703] Main Process
2012-12-01 21:22:30.033 AsyncToSync[71261:6103] Process: 1
2012-12-01 21:22:30.034 AsyncToSync[71261:4703] Main Process
2012-12-01 21:22:31.036 AsyncToSync[71261:6103] Process: 2
2012-12-01 21:22:31.037 AsyncToSync[71261:4703] Main Process
2012-12-01 21:22:32.038 AsyncToSync[71261:6103] Process: 3
2012-12-01 21:22:32.040 AsyncToSync[71261:4703] Main Process
2012-12-01 21:22:33.043 AsyncToSync[71261:6103] Process: 4
2012-12-01 21:22:33.044 AsyncToSync[71261:4703] Main Process
2012-12-01 21:22:33.080 AsyncToSync[71261:4703] finish.

これでデッドロックせずに最後まで処理を終えることができます。

コメント

  1. […] でした。 原因はセマフォを利用している部分。 対処方法はこちら(Objective-Cでスレッドをデッドロックさせずに非同期処理の終了を待つ方法。)を参考に以下のように書き換えました。 […]

タイトルとURLをコピーしました