Swift Concurrency 要諦(完全版)

389 Views

March 08, 25

スライド概要

Swift Concurrency の要所を整理してみたスライドです。

この話題をこれまでに「千歳ゆるい勉強会vol.4」と「関西モバイルアプリ研究会A #6」、そして「集まれSwift好き!Swift愛好会 vol.92 @ DeNA」で話してきましたけれど、時間の都合で盛り込めなかったところがあったのでそれを含めたスライドになります。

特に「千歳ゆるい勉強会vol.4」と「関西モバイルアプリ研究会A #6」で使用したスライドは、自分の理解不足から言葉の区別が甘かったところがあって修正した内容も含まれているので、改めてこちらを眺めてもらえると嬉しいです。

profile-image

正統派趣味人プログラマー。プログラミングとは幼馴染です。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

完全版 Swift Concurrency 要諦 熊谷友宏 @es_kumagai 2024/10/20 千歳ゆるい勉強会 vol.4 2024/11/29 関西モバイルアプリ研究会A #6 2025/03/04 集まれSwift好き!Swift愛好会 vol.92 @ DeNA

2.

熊谷 友宏 Tomohiro Kumagai ▶ Swift 言語が好み ▶ 小さな勉強会を開催(2014年9月27日より) ▶ 株式会社ゆめみさんで 熊谷さんのやさしい Swift 勉強会 開催 & コードレビュー • いろんな人のレビューをしたい機運が高まり中 • 言語仕様を学ぶことの効果の高さを実感中 ▶ プログラミングの楽しさを伝えていきたい

3.

熊谷 友宏 Tomohiro Kumagai ▶ 技術同人誌の制作 らしい表現を目指そう、Swift イニシャライザー大全 • Swift プログラマーのための新千歳空港入門 ▶ 同人誌即売会への出展 • ▶ 技術書典、おもしろ同人誌バザール、 コミックマーケット、コミティア(新規参入) プログラミングの楽しさを伝えていきたい

4.

Swift 5.5 に登場 → 6.0 で本格始動 Swift Concurrency ※ 今は Swift 6.0 が最新

5.

Swift Concurrency ▶ 並行処理を行うための仕組み(?) ▶ Swift でも async / await が使えるようになった(?) // 非同期関数を定義 func exec() async // 関数を非同期で実行 await exec()

6.

Swift Concurrency ▶ 並行処理を行うための仕組み(?) ▶ Swift でも async / await が使えるようになった(?) 本質 は // 非同期関数を定義 func exec() async // 関数を非同期で実行 await exec() そこでは ないらしい。

7.

Swift Concurrency 要諦

8.

Swift Concurrency とは ▶ 並行処理を安全に行うための仕組み ▶ async / await は その一環

9.

Swift Concurrency 本質 ▶ 並行処理を安全に行う ▶ データ競合の発生を未然に阻止 ▶ 競合状態の発生可能性を可視化 ※ 並行安全のコンパイラー支援が魅力

10.

Swift Concurrency データ競合 とは ▶ 非同期処理で 予期しないデータになる現象 ▶ ひとつのメモリーを複数箇所で同時に参照、 そこに書込処理が伴うときに発生することがある ※ Swift Concurrency によって、この発生が未然に阻止される

11.

Swift Concurrency 競合状態 とは ▶ 非同期処理で 予期しないデータになる現象 ▶ ある一連の処理を実行中に別の処理が並行して走ったときに 意図した計算結果にならないことがある ※ Swift Concurrency によって、この発生可能性が視覚化される

12.

並行処理の時代 ̶ データ競合の回避は 人類の夢 … ※ データ競合や競合状態は、マルチスレッドでは無視できない問題

13.

並行処理の時代 ̶ データ競合の回避は 人類の夢 … ▶ メモリーを共有しない ▶ シリアルキュー ▶ イミュータブルクラス ▶ ロックフリー ▶ ロック ※ データ競合や競合状態は、マルチスレッドでは無視できない問題 などなど

14.

並行処理の時代 ̶ データ競合の回避は 人類の夢 … ▶ メモリーを共有しない ▶ シリアルキュー ▶ イミュータブルクラス ▶ ロックフリー ▶ ロック ▶ Swift Concurrency などなど ※ データ競合や競合状態は、マルチスレッドでは無視できない問題 NEW

15.

人類の夢 Swift Concurrency

16.

Swift Concurrency 導入された概念 ▶ タスク • 非同期処理の実行単位 • 実行スレッドはシステムで管理 ▶ ▶ 中断ポイント • 非同期処理の結果を待つ • 実行スレッドをブロックせずに待つ 隔離領域 • データの保護単位 • 領域を越える値の扱いを制限 ※ Swift Concurrency は、この3つの概念を把握することが肝要

17.

Swift Concurrency 導入されたキーワード よくみる機能 知っておきたい機能 そのうち知ればいい機能 1. async 1. Task 1. @Sendable 2. await 2. Sendable 2. Actor 3. actor 3. @globalActor 4. @MainActor 4. GlobalActor 5. nonisolated 5. isolated 6. nonisolated(unsafe) 7. sending 8. #isolation などなど

18.

Swift Concurrency キーワードまわりの要所

19.

Swift Concurrency キーワードまわりの要所 よくみる機能

20.

Swift Concurrency よくみる機能 1. async ▶ 非同期での実行を前提とすることを印付ける • 内部の処理で、非同期処理の実行や 自身の処理を中断可能 • カレントタスクで実行 される • 隔離領域の切り替えには関与しない (期待する隔離領域は別の方法で指定) func method(value: String) async -> Int var property: String { get async } async let result = await exec()

21.

Swift Concurrency よくみる機能 2. await ▶ 非同期で実行することを印付ける • 式を非同期で実行し、その 処理完了まで中断 して待つ • カレントタスクで実行 する • 隔離領域を切り替えるかは呼出先に依存 let result = await method(value: "TEST") await print(property)

22.

Swift Concurrency キーワードまわりの要所 知っておきたい機能

23.

Swift Concurrency 知っておきたい機能 1. Task ▶ ブロック内が 非同期で実行されることを印付ける • ブロック内の処理を 新規タスクで開始 し、ここでは 処理を中断しない • ブロックに渡す値や戻り値は タスクを越えて渡される • 隔離領域を引き継ぐかは、状況次第 // 子タスクを形成 // キャンセル状態・隔離領域・優先順位を継承 Task { … } // 独立したタスクを形成 Task.detached { … }

24.

Swift Concurrency 知っておきたい機能 2. Sendable ▶ 並行安全であることを印付けるプロトコル • 並行安全な型だけに適用できる • インスタンスは、自由に 隔離領域 や タスクを越えられる • 並行安全性を コンパイラーで保証するか、プログラマーが保証するか選択可能 (スレッドセーフ) // 型の並行安全性はコンパイラーが保証してくれる struct Value: Sendable { … } // 型の並行安全性はプログラマーが保証しなければならない struct Object: @unchecked Sendable { … }

25.

Swift Concurrency 知っておきたい機能 3. actor インスタンス単位で隔離領域をつくる型 ▶ • インスタンス単位で 隔離領域 を形成 • 機能へのアクセスは 自身の隔離領域で実施させる • 複数箇所からの編集を伴う同時アクセスを阻止 actor Operator { var state: Value func execute() } let operator = Operator() Task { await operator.execute() }

26.

Swift Concurrency 知っておきたい機能 4. @MainActor ▶ メインアクター隔離であることを印付ける • メインスレッドによる保護を提供する隔離領域 で扱うことを示す • 処理の実行を メインスレッド に限定し、複数箇所からの同時アクセスを阻止 • プロパティー・関数・メソッド・型・タスク などに付与して使う @MainActor func something() @MainActor struct View { } @MainActor var value: Value Task { @MainActor in }

27.

Swift Concurrency 知っておきたい機能 5. nonisolated 隔離領域の外側で動作することを印付ける ▶ • 隔離領域の外側で実行可能 なメソッドになる • 同期的に呼び出せる • 隔離領域で保護されたプロパティーにはアクセスできない (所属する隔離領域を継承しない) (明示的に async にした場合は、非同期呼出) @MainActor struct MyCommand { nonisolated func printVersion() { … } }

28.

Swift Concurrency キーワードまわりの要所 そのうち知ればいい機能

29.

そのうち知ればいい機能 1. @Sendable 関数が並行安全であることを印付ける ▶ • 並行安全な要素で構成された関数 だけに付与できる • 隔離領域 や タスクを越えた安全な受け渡しと実行が可能 @Sendable func something(_ value: Int) -> Int { } let invoke: @Sendable (_ value: Int) -> Int = { value in }

30.

そのうち知ればいい機能 2. Actor 何らかのアクターであることを印付けるプロトコル ▶ • すべてのアクター型が、暗黙的に準拠する • これを継承したプロトコルは、アクター型だけに適用可能 - 内包する宣言は、所属するアクター隔離領域の保護下に置かれる想定 protocol Driver: Actor { var state: State { get } func discover() } func makeActor() -> some Actor { }

31.

そのうち知ればいい機能 3. @globalActor 型が大域的な隔離領域を構成するのを印付ける ▶ • 型単位の隔離領域 を提供 • MainActor も大域隔離領域のひとつ • 大域アクター自体は、インスタンスでは隔離領域を形成しない @globalActor struct Processing { static let shared: MyActor } @Processing var state: State @Processing func convert() @Processing class Processor { } Task { @Processing in

32.

そのうち知ればいい機能 4. GlobalActor ▶ 大域アクターとして振る舞えることを印付けるプロトコル • すべての大域アクター型が、暗黙的に準拠する • これを継承したプロトコルは、大域アクター型にだけ適用可能 - 自身がアクターであることまでは求めていない - 自身のメソッドやプロパティーは、隔離領域の保護下に置かない • 今のところは、使える機会がないかもしれない

33.

そのうち知ればいい機能 5. isolated 関数が 指定した隔離領域下で動作することを印付ける ▶ • 指定したアクター隔離領域内で関数を処理 する • 指定した隔離領域からは同期実行、その他からは非同期実行 • 指定した隔離領域が保護するプロパティーの直接アクセスが可能 func execute(on actor: isolated Controller) { } await execute(on: controller)

34.

そのうち知ればいい機能 6. nonisolated(unsafe) 隔離領域の外側からアクセスできることを印付ける ▶ • 隔離領域の外側から操作可能 なプロパティーになる • 隔離領域の保護によらない、同期的な読み書きが可能 • アクター保護下にあったプロパティーに指定したときは、 (所属する隔離領域を継承しない) データ競合からの保護はプログラマーが行う必要あり(コンパイラーは関与しない) @MainActor struct Process { nonisolated(unsafe) var isRunning: Bool }

35.

そのうち知ればいい機能 7. sending 隔離領域を越えて受け渡すことを印付ける ▶ • 引数や戻り値の型に指定できる • 指定された値は Sendable でなくても タスクや隔離領域を越えられる • 値を渡したあとは、その値にはアクセス禁止 (Sendable な値なら引き続き利用可能) func pass(_ x: sending Object) async { Task { print(x) } }

36.

そのうち知ればいい機能 8. #isolation ▶ 現在の隔離領域を取得するマクロ • 現在の隔離領域を形成するインスタンスを取得 • 隔離領域内で実行されていなければ nil になる • 実行する隔離領域の指定にも使用可能 print(#isolation) if #isolation === ProcessingActor.shared { … } func execute(on actor: isolated (some Actor)? = #isolation)

37.

Swift Concurrency 概念まわりの要所

38.

Swift Concurrency タスクと隔離領域 ▶ タスク内の処理は 逐次処理(並行処理されない) ▶ 複数のタスクは 同時並行で処理 される ▶ ひとつの 隔離領域で、同時に処理できるのはひとつ だけ ▶ 隔離領域を使用中は、その処理が 中断 または 終了 するまで 次の処理は実施されない(呼出元は 中断して待つ)

39.

Swift Concurrency 中断ポイントと再入可能性 ▶ async な機能は、await による 非同期実行(中断の可能性あり) ただし 同じ隔離領域での動作なら await なし の同期実行(即時実行) ▶ await では、結果が得られるまでタスクは処理を中断 ▶ 中断している間は、その隔離領域 で 別のタスクを実行可能 Task { @MainActor in Task { @MainActor in actionA() await someActor.someAction() actionB() } ここで中断されたときに 別のタスクを実行開始 actionX() actionY() }

40.

まとめ

41.

Swift Concurrency まとめ Swift Concurrency の本質 データ競合を起こさせないためにのみ存在する ▶ 思いのほか、追加機能はたくさん ▶ コードに印を付けて、データ競合の可能性を Swift が 見つけられるようにするのが Swift Concurrency の醍醐味

42.

Enjoy! Swift Thank you 熊谷友宏 @es_kumagai