ゆるい感じのプログラムを書きたい。

プログラムの敷居を下げて、多くの人が開発出来るように色々書いていきます!

【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が以下の管理を自動的に行います。
 ・適切なスレッドへのタスクの振り分け
 ・アプリケーション内部で自動生成されたキュー
 ・キューに追加されたタスクを実行するスレッド


イメージはこんな感じです。
f:id:kassans:20140313125458p:plain


dispatch queueの種類

▼メインディスパッチキュー(メインキュー)
 ・アプリケーションのどこからでも利用できる直列(シリアルキュー)
 ・アプリケーションのメインスレッドで実行
 ・UIの描画が更新はこのキューで行う必要がある。


▼直列SerialDispatchQueue(直列なキュー)
 ・タスクを同時に一つずつ追加された順に実行する仕組み
 ・タスクの実行が終わると次のタスクの実行を行う。
 ・タスクは他のキューと独立したスレッド上で動作。


■SerialDispatchQueueのイメージ

f:id:kassans:20140313125502p:plain


▼並列ConcurrentDispatchQueue(並列なキュー)
 ・複数のタスクを同時に実行する。
 ・実行の順番はキューに追加した順番になるが、終了のタイミングの順序は
  保証されない。
 ・同時に実行するタスクの数はシステムの状況に応じて変化する。
 ・アプリケーションが所有するglobal queueとアプリケーション内部で生成する
  queueを持てる。
 ・キューには優先度をつけることができる 


■ConcurrentDispatchQueueのイメージ

f:id:kassans:20140313125505p:plain

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);


次回は、実装編でプログラムでの記述方法などを記載します。