🦸

Task.detachedで投げっぱなしジャーマンせずにawaitする

2022/01/28に公開

Task.detachedで分離されたTaskを作成することができます。
WWDC21のExplore structured concurrency in Swift!ではMainActorでdelegateを実装し、画像を取り出したついでにMainActorとは分離されたTaskでキャッシュすることを紹介されていました。

WWDC21: Explore structured concurrency in Swift!
https://developer.apple.com/videos/play/wwdc2021/10134/

ここでの分離されたTaskはメインとは別スレッドで動作しています(もしかして仕様としてはコルーチンで並列動作するのであって、別スレッドになっているのは動作上そうなってるだけかもしれません)。

つまり、メインスレッドとは別にキャッシュ作成は処理が動作しています。処理の完了もエラーもハンドリングしないので処理の投げっぱなしジャーマンになっている。というわけです。

でも、Task.detachedもTaskなのでawaitができます。つぎのサンプルはSendableなIntをランダムで生成し結果を取得するようにしてみました。

@MainActor
struct Sample {
    func start() {
        print("1_0")
        Task {
            print("2_0")
            let task: Task<Int, Error> = Task.detached(priority: .background) {
                assert(!Thread.isMainThread)
                print("3_0")
                try await Task.sleep(nanoseconds: 10)
                print("3_1")
                return Int.random(in: 0..<10)
            }
            let value = try await task.value
            print("value:", value)
            print("2_1")
        }
        print("1_1")
    }
}

Task {
    await Sample().start()
}

出力は次の通り。taskをawaitしてそれから処理が終わっています。

1_0
1_1
2_0
3_0
3_1
value: 6
2_1

ただ、Task.detachedあんまり使うのはどうかとも思います。あきらかに構造化しないようなTaskを作成する場合には使えるね、という話でした。

関連

https://zenn.dev/yimajo/articles/9b3b1ac317d288

Discussion