しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

【Swift】Concurrencyで並列処理を実現する

Swift5.5から登場したConcurrencyで並列処理を実現してみました。

まずは下のように一定時間待ってからprintするメソッドを作ります。

func testFunction() async {
    try? await Task.sleep(nanoseconds: 1_000_000_000)
    print(1)
    try? await Task.sleep(nanoseconds: 2_000_000_000)
    print(2)
}

上のメソッドを3つ平行に動かす方法は下の通りです。

async let f1: () = testFunction()
async let f2: () = testFunction()
async let f3: () = testFunction()
_ = await [f1, f2, f3]

実行すると下のように3つの処理が並行して動いているログが表示されます。

viewDidLoadのような通常のメソッドから非同期な処理を呼びたい場合、下のようにTaskを使います。

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        print("viewDidLoad1")        
        Task {
            async let f1: () = testFunction()
            async let f2: () = testFunction()
            async let f3: () = testFunction()
            _ = await [f1, f2, f3]
        }
        print("viewDidLoad2")
    }
}

実行すると下のようなログになります。
Task内の処理を待たずにviewDidLoad内の処理が最後まで動いている事が分かりました。

実行時のスレッドも確認してみました。
下のようなTask内の処理はメインスレッドになります。

Task {
    print(Thread.isMainThread) // → true
    async let f1: () = testFunction()
    async let f2: () = testFunction()
    async let f3: () = testFunction()
    _ = await [f1, f2, f3]
}

下のメインスレッドでしかできない操作(viewの背景色変更)を試した所、エラーなく動かす事ができました。

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        Task {
            view.backgroundColor = .blue
        }
    }
}

バックグラウンドで動かしたい場合、下のようにTask.detachedを使います。

Task.detached {
    print(Thread.isMainThread) // → false
    async let f1: () = self.testFunction()
    async let f2: () = self.testFunction()
    async let f3: () = self.testFunction()
    _ = await [f1, f2, f3]
}

ただ、その場合でも呼び出し先のメソッドはメインスレッドになるので注意が必要です。

func testFunction() async {
    print(Thread.isMainThread) // → true
}

もし呼び出し先でもバックグラウンドにしたい場合、下のようにTask.detachedメソッドを使う必要があります。

func testFunction() async {
    await Task.detached {
        print(Thread.isMainThread)
    }.value
}