4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Swift】非同期処理「GCD」について

Posted at

#GCD とは
GCDとは、非同期処理を容易にするための技術です。
GCDではキューを通じて非同期処理を行い、直接スレッドを管理することはありません。

スレッドの管理はシステムが担当し、
CPUのコア数や負荷の状態などを考慮して自動的に処理を最適化します。

つまり、処理の並列数やスケジューリング、
どの処理をどのスレッドで行うかなどをプログラマーが考える必要がありません。

プログラマーが考えることと言えば、
タスクをキューに追加することだけです。

スレッドはその都度生成される訳ではなく、
事前に準備されていたスレッドの中から空いているものにタスクが振られます。

この時に空いているスレッドが存在しなかった場合は、
スレッドが空くまで待機することになります。

このような管理の手法をスレッドプールと言います。

##実装方法
GCDのキューはディスパッチキューと言います。
ディスパッチキューはタスクの実行方式によって2種類に分類されます。

・直列ディスパッチキュー
->現在実行中の処理の終了を待ってから次の処理を実行する
・並列ディスパッチキュー
->現在実行中の処理の終了を待たずに次の処理を実行する

###ディスパッチキューの取得
GCDの既存のディスパッチキューとして、
1つもメインキューと複数のグローバルキューを提供しています。

メインキューは、メインスレッドでタスクを実行する直列ディスパッチキューです。

メインキューの取得方法はmainクラスプロパティを使用して取得します。


import Dispatch

let queue = DispatchQueue.main   // メインディスパッチキューを取得

ユーザーインターフェースの更新、つまりユーザの操作は、
常にメインキューで実行されています。

他のディスパッチキューで実行された非同期処理の結果を反映させる場合、
メインキューを取得してタスクを追加することになります。

グローバキューは並列ディスパッチキューのことを指し、
実行優先度を指定して取得します。

その実行優先度のことをQoSと言い優先度の高い順に下記の5つがあります。

① userInteractive
アニメーションの実行などの、
即座に実行されなければフリーズしているように見える処理に用いる

② userInitiated
何かをタップした場合などのユーザからの入力を受けて実行される処理に用いる

③ default
QoSが何も指定されていない場合に利用される

④ utility
視覚的な情報の更新を伴いながらも、
即時の結果を要求しない処理に用いる

⑤ background
バックアップなどの目に見えないところで行われ、
数分から数時間かかっても問題ない処理に用いる

###タスクの追加
取得したディスパッチキューにタスクを追加するには、
DispatchQueue型のasync(execute:)メソッドを用います。

直列ディスパッチキューも並列ディスパッチキューもタスクの追加方法は同様です。

次のサンプルコードでは、
並列ディスパッチキューに対してasync(execute:)メソッドを呼び出して
非同期処理を行っているコードになります。

メインスレッドでのfor-in文に加え、非同期処理のfor-in文も行っています。
並列で処理を行っているので実行結果がごちゃごちゃになります。


import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue.global(qos: .userInitiated)
queue.async {
    for item in 0..<5 {
        print("非同期処理:\(item)")
    }
}

for item in 0..<5 {
    print("同期処理:\(item)")
}

実行結果
同期処理:0
非同期処理:0
同期処理:1
非同期処理:1
同期処理:2
非同期処理:2
同期処理:3
同期処理:4
非同期処理:3
非同期処理:4

今回のQoSはuserInitiatedでしたが、
これをuserInteractiveにするとまた結果が変わるります。


import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue.global(qos: .userInteractive)
queue.async {
    for item in 0..<5 {
        print("非同期処理:\(item)")
    }
}

for item in 0..<5 {
    print("同期処理:\(item)")
}

実行結果
非同期処理:0
同期処理:0
非同期処理:1
同期処理:1
非同期処理:2
非同期処理:3
同期処理:2
非同期処理:4
同期処理:3
同期処理:4

userInitiatedより優先されているのがわかると思います。
少し結果が変わってくるので、
非同期処理を行う際はその時に必要な並列処理の優先順位を明確にしましょう。

##利用すべきとき

GCDを利用する場面としては単純な非同期処理の実装になります。
その理由は、タスクをクロージャとして表現できるためです。

メインスレッドから時間のかかる処理を別のスレッドに移し、
その処理が終わったらメインスレッドに通知する場面を例にします。


import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue.global(qos: .userInteractive)
queue.async {
    print("非同期処理実行-1")

    let queue = DispatchQueue.main
    queue.async {
        print("メインスレッド実行")
    }

    print("非同期処理実行-2")
}

非同期処理が行われてからメインスレッドに制御が戻っていることがわかります。
このようなシンプルな非同期処理は楽に実現することができます。

逆に、タスクどうしの依存関係の定義や、条件に応じたタスクのキャンセルなどは、
OperationQueueクラスなどを利用しましょう。

以上でディスパッチキューについての説明は終わります。

単純な非同期処理についてはこれで十分かと思いますが、
複雑な処理を行う場合はOperationQueueクラスなどを利用しましょう。

最後までご覧いただきありがとうございました。

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?