【iPhoneアプリ】これを使えるようにならないと「マルチスレッド」について 概要編
今回は、「マルチスレッド」についてです。
iOSの開発を行うにあたり、必要不可欠だと思い記載します。
今回は概要編と実装編の2部構成で記載します。
また、mixiのiOSTraningのサイトを参考にして
自分自身が見返した際に分かるような形に編集しています。
■参考サイト
https://github.com/mixi-inc/iOSTraining/wiki/8.2-Grand-Central-Dispatch
iOSのマルチスレッドについて
マルチスレッドを行うには以下の方法があります。
NSThread
・スレッドを立てて、そのスレッドの中で処理を行う。
・スレッドの管理やキューイングなどの管理はアプリケーションが行う
GCD (dispatch_xxx)
・スレッドの管理などをOSレベルで実装。
・処理をしたいタスクをBlockで渡す。
・渡されたタスクはキューに挿入されて逐次実行される。
最近は、GCDがよくサイトに記載されています。
NSThreadだとスレッドの処理など、自前で書かないといけないのでコードが冗長に
なってしまい大変です。
そこで、iOSの並列処理実行技術のGCDについて記載して行きます。
並列処理の必要性
まず、GCDの紹介の前に、並列の処理の必要性について記載します。
以下のケースの場合に並列処理が必要となります。
・通信処理に時間が掛かってしまう場合。
・ファイルへのアクセス、DBへのアクセスで時間がかかってしまう場合。
上記の処理を、メインスレッド(画面描画スレッド)で行った場合。
画面描画やユーザーのアクションへの反応ができなくなり、"固まった"状態になります。
また、iPhone5以降ではApple A6プロセッサでコア2つ搭載していますが
並列処理を行わないと、コア1つを持て余す事になります。
その事から、待ち時間の発生する処理、計算に時間のかかる処理などは
複数のスレッドを用いて並列に処理を行うことでCPUのパワーを持て余さず
使用する事ができます。
GCDの紹介
iOSでは、並列処理を簡単に行う事が出来る仕組みとして、GCDがあります。
これは、iOS4以降で利用可能です。
では、紹介して行きます。
■dispatch queueとタスクについて
・dispatch queue(ディスパッチキュー)と呼ばれる入れ物にタスクを追加
・dispatch queue(ディスパッチキュー)に追加されたタスクは、FIFOで順番に実行
・タスクの実行は、メインスレッドとは別のスレッドで実行
・タスクはBlocksとして登録する
その他、このような特徴があります。
■管理するキューは以下の種類があります。
・アプリケーション独自に生成(プライベートキュー)
・アプリケーション起動時にシステム内で自動的に生成(グローバルキュー)
■OSが以下の管理を自動的に行います。
・適切なスレッドへのタスクの振り分け
・アプリケーション内部で自動生成されたキュー
・キューに追加されたタスクを実行するスレッド
イメージはこんな感じです。
dispatch queueの種類
▼メインディスパッチキュー(メインキュー)
・アプリケーションのどこからでも利用できる直列(シリアルキュー)
・アプリケーションのメインスレッドで実行
・UIの描画が更新はこのキューで行う必要がある。
▼直列SerialDispatchQueue(直列なキュー)
・タスクを同時に一つずつ追加された順に実行する仕組み
・タスクの実行が終わると次のタスクの実行を行う。
・タスクは他のキューと独立したスレッド上で動作。
■SerialDispatchQueueのイメージ
▼並列ConcurrentDispatchQueue(並列なキュー)
・複数のタスクを同時に実行する。
・実行の順番はキューに追加した順番になるが、終了のタイミングの順序は
保証されない。
・同時に実行するタスクの数はシステムの状況に応じて変化する。
・アプリケーションが所有するglobal queueとアプリケーション内部で生成する
queueを持てる。
・キューには優先度をつけることができる
■ConcurrentDispatchQueueのイメージ
dipatch queueへのタスク処理追加
dipatch queueのタスク追加は以下の関数を使います。
■dispatch_async
▼特徴
・blockで定義された処理をqueueに追加する。
・非同期処理でタスクの実行完了を待たず、次の制御に進む。
▼記述方法
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
■dispatch_sync
▼特徴
・blockで定義された処理をqueueに追加する。
・同期処理でタスクの実行が完了するまで制御は止まる。
▼記述方法
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)
引数の説明
queue
処理をキューイングしたいキューを指定
block
処理したい内容を記述したブロック
プログラムで書くとこんな感じです。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ NSLog(@"Hello GCD!"); });
キューの生成と取得
■メインキュー
・メインディスパッチキューを取得して利用する
・直列(シリアル)キュー
▼取得方法
dispatch_queue_t dispatch_get_main_queue(void);
■グローバルキュー
・優先度が異なる、4つのグローバルキューが生成されているので、
アプリケーションのどこからでも取得し利用する。
・並列(コンカレント)キュー
▼取得方法
dispatch_queue_t dispatch_get_global_queue(long priority, unsigned long flags);
priorityの設定
以下のいずれかのキーを指定
キー名 | 優先度 |
---|---|
DISPATCH_QUEUE_PRIORITY_HIGH | 優先度高 |
DISPATCH_QUEUE_PRIORITY_DEFAULT | 優先度中 |
DISPATCH_QUEUE_PRIORITY_LOW | 優先度低 |
DISPATCH_QUEUE_PRIORITY_BACKGROUND | 優先度バックグラウンド |
flagsの設定
将来の拡張に備えて予約されています。当面は常に0を渡すようにしてください。
■プライベートキュー
・アプリケーションの一部のみで利用されるキューを生成して利用する事も可能です。
・直列(シリアル)キュー、並列(コンカレント)キューどちらも可能
▼生成方法
dispatch_queue_t dispatch_queue_create( const char *label, dispatch_queue_attr_t attr);
labelの設定
キューのラベルを付けます。逆DNS記法でつけることが推奨されています。
※デバッグを行った時のスタックトレースなどに出力されます。
dispatch_queue_attr_tの設定
キューのタイプが直列か並列を指定します。(iOS5以降対応)
キュータイプ | キー名 |
---|---|
直列 | DISPATCH_QUEUE_SERIAL |
並列 | DISPATCH_QUEUE_CONCURRENT |
まとめ
グローバルキュー、メインキューでは
タスクをエンキューしても実際にどの順番で実行されるか不明
プライベートなキューでは
必ずキューの先頭からタスクを実行していくのでシリアルなキューを実現
しかし、プライベートなキューを作成すると
Retainカウントが1なので 最後にリリースをすることを忘れずに
▼以下のコードでリリース
dispatch_release(high);
次回は、実装編でプログラムでの記述方法などを記載します。