【iPhoneアプリ】これを使えるようにならないと「マルチスレッド」について 実装編
今回も、「マルチスレッド」についてです。
前回、概要編との事で記載していましたが
今回は実際にプログラムを書いて、動かしてみる実装編を記載して行きます。
GCD実用例の前準備
GDCを用いた実用例の紹介の前に以下の準備をします。
・スレッドテスト開始用ボタンの描画
・スレッドテスト開始用ボタンのアクション処理
まず、始めに「スレッドテスト開始用ボタン」を以下のコードを
記述して描画させます。
■ViewController.m
#import "ViewController.h" @implementation ViewController{ MultiThread *Multi; } - (void)viewDidLoad { [super viewDidLoad]; Multi = [[MultiThread alloc] init]; UIButton* bt = [Multi ThreadButton]; bt.center = self.view.center; self.view.backgroundColor = [UIColor whiteColor]; [self.view addSubview:bt]; }
次に「スレッドテスト開始用ボタン」のアクション処理を
以下のコードを記述して行います。
■MultiThread.h
#import <Foundation/Foundation.h> @interface MultiThread : NSObject{ UIButton* bt; } - (UIButton*)ThreadButton; @end
■MultiThread.m
#import "MultiThread.h" @implementation MultiThread - (UIButton*)ThreadButton{ bt = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [bt setTitle:@"テスト" forState:UIControlStateNormal]; bt.frame = CGRectMake(0,0,200,200); [bt addTarget:self action:@selector(thread_Button:) forControlEvents:UIControlEventTouchUpInside]; return bt; } - (IBAction)thread_Button:(UIButton*)sender { //スレッドの処理をこの部分に記載します。 } @end
GCDの実用例
実用例として以下の項目を記載します。
・並列なキューで同期処理(dispatch_sync)
・並列なキューで非同期処理(dispatch_async)
・直列なキューで同期処理(dispatch_sync)
・直列なきユーで非同期処理(dispatch_async)
■並列なキューで同期処理(dispatch_sync)
・並列なキューの為、実行完了する順番は保証されない。
・dispatch_syncの為、別スレッド処理が完了するまで、次のスレッドは処理しない。
・dispatch_syncの為、全体のキューの処理完了を待って、次の処理を行います。
プログラムはこんな感じです。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); for (NSInteger i = 0 ; i < 100; ++i) { dispatch_sync(globalQueue, ^{ NSLog(@"%d", i); }); } NSLog(@"here!!"); }
結果はこんな感じです。
labo[6961:70b] 96 labo[6961:70b] 97 labo[6961:70b] 98 labo[6961:70b] 99 labo[6961:70b] here!!
追加したタスクが実行完了するまで次に進まない為、"here!!"は最後に出力されます。
イメージはこんな感じです。
また、並列なのに順番に処理をしているイメージはこんな感じです。
▼使いどころ
・グローバルキューで複数のスレッドで順番通りにタスクを処理させ
後ほど、処理を行ったタスクをメインキューで使う必要がある場合に使います。
■並列なキューで非同期処理(dispatch_async)
・並列なキューの為、実行の順番は保証されません。
・dispatch_asyncの為、別スレッド完了を待たず、スレッドは処理します。
・dispatch_asyncの為、全体のキューの処理完了を待たず、次の処理を行います。
プログラムはこんな感じです。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); for (NSInteger i = 0 ; i < 100; ++i) { dispatch_async(globalQueue, ^{ NSLog(@"%d", i); }); } NSLog(@"here!!"); }
結果はこんな感じです。
labo[6976:70b] here!! labo[6976:1303] 0 labo[6976:3807] 1 labo[6976:3a03] 2 labo[6976:3b03] 3
順番に実行され"here!!"が全てのタスクの完了より前に来ています。
イメージはこんな感じです。
▼使いどころ
・あるタスクで処理に時間もかかり、その後の処理でこのタスクの結果を
用いないといったケースで利用します。
■直列なキューで同期処理(dispatch_sync)
・直列なキューの為、タスクが同時に一つしか実行しないので競合が起きない。
・dispatch_syncの為、全体のキューの終了完了まで次のステップに進みません。
プログラムはこんな感じです。
dispatch_queue_t privateQueue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_SERIAL); for (NSInteger i = 0 ; i < 100; ++i) { dispatch_sync(globalQueue, ^{ NSLog(@"%d", i); }); } NSLog(@"here!!"); }
結果はこんな感じです。
labo[1812:70b] 96 labo[1812:70b] 97 labo[1812:70b] 98 labo[1812:70b] 99 labo[1812:70b] here!!
出力は順番になっており、追加したタスクが実行完了するまで次に進まない為、
"here!!"は最後に出力されています。
イメージはこんな感じです。
▼使いどころ
・ファイルはDBなど不整合が起きうる箇所で、かつその読み込んだ内容を
次で使う場合に使います
■直列なキューで非同期処理(dispatch_async)
・直列なキューの為、タスクが同時に一つしか実行しないので順番が前後しません。
・dispatch_asyncの為、全体のキューの処理完了を待たず、次の処理を行います。
プログラムはこんな感じです。
dispatch_queue_t privateQueue = dispatch_queue_create("jp.test.sample", DISPATCH_QUEUE_SERIAL); for (NSInteger i = 0 ; i < 100; ++i) { dispatch_async(globalQueue, ^{ NSLog(@"%d", i); }); } NSLog(@"here!!"); }
結果はこんな感じです。
labo[1789:70b] here!! labo[1789:1303] 0 labo[1789:1303] 1 labo[1789:1303] 2 labo[1789:1303] 3
出力は順番になっており、"here!!"が先に表示され、タスクを追加し終わると
次に進んでます。
イメージはこんな感じです。
▼使いどころ
・処理内容に一意性が求められる時に使います。
・例えばファイルへのアクセスなどが該当します。
・ファイルへのアクセスなどは時間がかかるのでasyncで非同期実行する
といった使い方ができます。
メインスレッドとグローバルスレッドの組み合わせ方
次に、メインスレッドとグローバルスレッドの組み合わせを紹介していきます。
以下のように処理分散させたい場合
・メインスレッドで描画処理
・グローバルスレッドで別処理
こんな感じのプログラムを書いてみます。
dispatch_queue_t main = dispatch_get_main_queue(); dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_async(globalQueue, ^{ int count = 0, i; //描画に必要な処理1 for(i = 0; i < 5; i++){ NSLog(@"HeavyProcessing_1 = %d", count); count++; } //メインスレッドで途中を描画 dispatch_async(main, ^{ NSLog(@"Main_1 = %d", count); }); //描画に必要な処理2 for(i = 0; i < 5; i++){ NSLog(@"HeavyProcessing_2 = %d", count); count++; } //メインスレッドで描画完了 dispatch_async(main, ^{ NSLog(@"Main_2 = %d", count); }); }); //通常の処理 NSLog(@"here!!");
結果はこんな感じです。
labo[1969:70b] here!! labo[1969:1303] HeavyProcessing_1 = 0 labo[1969:1303] HeavyProcessing_1 = 1 labo[1969:1303] HeavyProcessing_1 = 2 labo[1969:1303] HeavyProcessing_1 = 3 labo[1969:1303] HeavyProcessing_1 = 4 labo[1969:70b] Main_1 = 5 labo[1969:1303] HeavyProcessing_2 = 5 labo[1969:1303] HeavyProcessing_2 = 6 labo[1969:1303] HeavyProcessing_2 = 7 labo[1969:1303] HeavyProcessing_2 = 8 labo[1969:1303] HeavyProcessing_2 = 9 labo[1969:70b] Main_2 = 10
グローバルスレッドがasync(非同期処理)のため、先に通常処理が行われ
次にグローバルスレッドの処理が行われています。
グローバルキューでは、重い処理を行った後にメインスレッドが
呼ばれている事が分かります。
このため、グローバルスレッド「重たい処理」とメインスレッド「描画処理」の
分散処理の実装が可能となります。