iOSマルチスレッドNSThread

5038 ワード

1.スレッドの概要
非常に重いタスクを処理する必要がある場合、プライマリ・スレッドの実行をブロックすることを避けるために(プライマリ・スレッドは主にユーザーのインタラクションと関連イベントの処理を担当する)、スレッドを使用する必要があります.スレッドを使用して、大きなタスクをマルチコアマシン上で同時に実行する小さなタスクに分割すると、プログラムのパフォーマンスを大幅に向上させることができます.NSTreadは、実行スレッドの管理を提供してくれます.
2.スレッドオーバーヘッド
スレッドにはメモリとパフォーマンスのオーバーヘッドが必要です.メモリオーバーヘッドにはシステムカーネルメモリとアプリケーションメモリが含まれます.スレッドを管理および調整するために使用されるカーネル構造は、カーネルに格納されます.スレッドのスタックスペースと各スレッドのデータは、プログラムのメモリスペースに格納されます.メモリを占有するこれらの構造の大部分は、スレッドが作成されたときに生成され、初期化されます.カーネルと対話するため、このプロセスは非常に時間がかかります.スレッドの作成にかかるコストは、次のとおりです( ).
カーネルデータ構造:約1 KB 占有領域:主スレッドは約1 MB、第2スレッドは約512 KB スレッド作成時間:約90ミリ秒もう1つのオーバーヘッドは,プログラム内のスレッド同期のオーバーヘッドである.
3.スレッドの作成
NSThreadを使用してスレッドを作成するには、次の4つの方法があります.
NSThreadクラスメソッド+detachNewThreadSelector:toTarget:withObject:スレッドを作成し、を実行する
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

NSObjectのスレッド拡張(NSThreadPerformAdditions)メソッド+performSelectorInBackground:withObject:スレッドの作成と実行
[object performSelectorInBackground:@selector(myThreadMainMethod:) withObject:nil];

NSThreadオブジェクトを作成し、startメソッドを呼び出してを実行します.
NSThread* aThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:) object:nil];
[aThread start];

NSThreadサブクラスを作成し、startメソッドをインスタンス化します.
#import 
@interface Thread : NSThread
@end
#import "Thread.h"
@implementation Thread
- (void)main
{
    @autoreleasepool
    {
        //do thread task
    }
}
@end//  
Thread *thread = [[Thread alloc] init];
[thread start];

4.スレッドパラメータの構成
4.1スレッドスタックスペースの構成
スタックスペースは、スレッドの作成前に設定する必要があるスレッドに作成されたローカル変数を格納するために使用されます.スレッドを作成する第1および第2の方法は使用できません.NSThreadのstartメソッドを呼び出す前にsetStackSize:を通過する 新しいスタックスペースのサイズを設定します.
4.2スレッドのローカルストレージの構成
各スレッドは、スレッドのどこでも取得できる辞書を維持します.NSThreadのthreadDictionaryメソッドを使用してNSMutableDictionaryオブジェクトを取得し、必要なフィールドとデータを追加できます.
4.3スレッドのDetached状態の設定
NSThreadで作成されたスレッドはすべてDetachedです.joinableスレッドを作成する場合は、POSIXスレッドインタフェースでのみ作成できます.
4.4スレッドの優先度の設定
新しいスレッドごとにデフォルトの優先度があります.システムのカーネルスケジューリングアルゴリズムは、スレッドの優先度に基づいてスレッドの実行順序を決定する.通常、スレッドの優先度を変更しないでください.一部のスレッドの優先度を上げると、低優先度のスレッドが実行されない可能性があります.アプリケーションに高優先度のスレッドと低優先度のスレッドが相互作用している場合、低優先度のスレッドが実行されないため、他のスレッドの実行をブロックする可能性があります.これにより、アプリケーションにパフォーマンスのボトルネックが発生します.スレッド優先度は、NSThreadのsetThreadPriority:メソッドで0.0~1.0のdoubleタイプ、1.0の順に設定できます.
5.スレッドエントリの完了
5.1 Autorelease Poolの作成
スレッドの入り口でAutorelease Poolを作成し、スレッドが終了したときにこのAutorelease Poolを解放する必要があります.これにより、スレッドに作成されたautoreleaseオブジェクトは、スレッドの終了時に解放され、遅延解放によるプログラムのメモリ消費が多すぎることを回避できます.長寿命スレッドの場合は、この目的を達成するために、より多くのAutorelease Poolを作成する必要があります.たとえばスレッドにrun loopが使用されている場合、反復のたびにAutorelease Poolを作成する必要があります.ARC以外でAutorelease Poolコードを作成するには、次のようにします.
- (void)myThreadMainRoutine
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
    // Do thread work here.
    [pool release];  // Release the objects in the pool.
}

ARC環境でのコードは次のとおりです.
- (void)myThreadMainRoutine
{
    @autoreleasepool
    {
        //do thread task
    }
}

5.2異常処理の設定
アプリケーションが例外をキャプチャして処理できる場合は、独自に作成したスレッドが例外をキャプチャする必要があります.そうしないと、スレッドに問題が発生した場合、アプリケーションはCrashになります.
5.3 Run Loopの設定
スレッドを作成するときに2つの選択肢があります.1つは、スレッドが長いタスクを実行してからタスクが終了したときに終了することです.もう1つは、スレッドがループに入り、動的に到着したタスクを処理することです.各スレッドのデフォルトにはNSRunloopがあり、メインスレッドはデフォルトで開き、他のスレッドは手動で開きます.NSRunloopの理解と次の章の使用について議論する.
6.終了スレッド
スレッドを終了するにはPOSIXインタフェースでスレッドを直接殺さないほうがいい.このような乱暴な方法では、システムがスレッドで使用されているリソースを回収できず、メモリが漏れ、プログラムの実行に影響を与える可能性がある.スレッドを終了する最善の方法は、スレッドにキャンセルメッセージと終了メッセージを受信させることです.これにより、スレッドは、メッセージを受信したときに既存のリソースをクリーンアップし、メモリの漏洩を避けることができます.このスキームの1つの実施形態は、NSRunloopのinput sourceを使用してメッセージを受信し、毎回のNSRunloopループは、終了条件がYESであるか否かをチェックし、YESである場合はループ回収リソースを終了し、NOである場合は、次のNSRunloopループに進む.例コードは次のとおりです.
- (void)threadMainRoutine
{
    BOOL moreWorkToDo = YES;
    BOOL exitNow = NO;
    NSRunLoop* runLoop = [NSRunLoop currentRunLoop];

    // Add the exitNow BOOL to the thread dictionary.
    NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
    [threadDict setValue:[NSNumber numberWithBool:exitNow] forKey:@"ThreadShouldExitNow"];

    // Install an input source.
    [self myInstallCustomInputSource];

    while (moreWorkToDo && !exitNow)
    {
        // Do one chunk of a larger body of work here.
        // Change the value of the moreWorkToDo Boolean when done.

        // Run the run loop but timeout immediately if the input source is not waiting to fire.
        [runLoop runUntilDate:[NSDate date]];

        // Check to see if an input source handler changed the exitNow value.
        exitNow = [[threadDict valueForKey:@"ThreadShouldExitNow"] boolValue];
    }
}
参照リンク:
         1. Threading Programming Guide 
         2. Concurrency Programming Guide