Upgrade to Pro — share decks privately, control downloads, hide ads and more …

UniTaskの使い方2020 / UniTask2020

UniTaskの使い方2020 / UniTask2020

2020/6/26 C# Tokyo オンライン「Unity 祭り」
https://csharp-tokyo.connpass.com/event/175469/

発表中に出てきた資料などのリンク

- UniTask入門
 https://learning.unity3d.jp/2974/

- さては非同期だなオメー!async/await完全に理解しよう
 https://learning.unity3d.jp/275/

- async/await のしくみ
 https://learning.unity3d.jp/1510/

- AsyncMessageBroker
 https://twitter.com/neuecc/status/1262578286493724672

torisoup

June 26, 2020
Tweet

More Decks by torisoup

Other Decks in Technology

Transcript

  1. UniTaskの使い方2020
    2020/6/26
    とりすーぷ

    View full-size slide

  2. 自己紹介
    • とりすーぷ(@toRisouP)
    • UnityでxR系の開発してます
    • 最近はVRChatで遊んでます
    • Microsoft MVP
    • Developer Technologies2018~

    View full-size slide

  3. 今回の主な内容
    • 非同期処理、async/awaitの解説
    • UniTaskについて

    View full-size slide

  4. 想定バージョン
    • Unity 2019.x系
    • UniTask 2.0.21

    View full-size slide

  5. 以前解説した資料
    • UniTaskについて説明した資料
    • UniTask入門
    • https://learning.unity3d.jp/2974/
    • UniTask2が出ていろいろ変わった部分がある
    • その部分も含めてアップデートしてます

    View full-size slide

  6. もくじ
    • 「非同期処理」
    • 「async/await」
    • 「UniTask」
    • 「UniTaskAsyncEnumerable」

    View full-size slide

  7. 非同期処理
    #とは

    View full-size slide

  8. 非同期処理とは
    • 非同期処理

    View full-size slide

  9. 非同期処理とは
    • 非同期処理

    View full-size slide

  10. 非同期処理とは
    • 非同期処理
    • そもそも「同期処理」って何?

    View full-size slide

  11. 同期処理
    • 処理(関数)の実行が終わるまでちゃんと待つもの
    • 呼び出した処理が終わるまで自分を一時停止する

    View full-size slide

  12. 非同期処理
    • 処理が終わるのを待たず先に元の処理に戻ってくるもの
    • 「処理の呼び出し終了」と「処理の完了」のタイミングが一致しないもの
    • 裏で別の処理を並行に実行しながら、元の処理を継続できる

    View full-size slide

  13. 非同期処理

    View full-size slide

  14. よくある勘違い
    • 非同期処理 = マルチスレッド処理

    View full-size slide

  15. よくある勘違い
    • 非同期処理 = マルチスレッド処理
    • これは間違い

    View full-size slide

  16. よくある勘違い
    • 非同期処理 = マルチスレッド処理
    • これは間違い
    • 非同期処理は「完了を待たずに呼び出し元にもどる処理」のこと
    • スレッドがどうこうとかいう話は本来は関係ない
    • マルチスレッド処理と非同期処理の相性がいいのは事実だけど、概念としては別物
    • なんならシングルスレッドのみを使っても非同期処理は実現できる

    View full-size slide

  17. 例:コルーチン
    • コルーチン
    • Unityのメインスレッドを使った非同期処理の機構
    • メインスレッドのみを使って非同期処理を書くこともできる

    View full-size slide

  18. メインスレッドのみを使ったコルーチン

    View full-size slide

  19. メインスレッドのみを使ったコルーチン
    「前に進む」という非同期処理を実行し、
    それが完了したらコールバックが実行される

    View full-size slide

  20. 例:イベント処理
    • 「イベント処理」は非同期処理の仲間
    • 「同じ非同期処理を何度も待ち続ける」ともみなすことができる
    • 「いつ完了するかわからない処理」を「何度も繰り返して待つ」

    View full-size slide

  21. イベント処理は非同期処理
    • OnCollisionEnterイベント
    • ぶつかるたびにこの関数が実行される

    View full-size slide

  22. イベント処理は非同期処理
    • 「ぶつかるまで待つ」という非同期処理を
    「繰り返し待ち続ける」とみなせる
    • async/awaitのコードに書き直しても動く(要:UniTask)

    View full-size slide

  23. 非同期処理 まとめ
    • 非同期処理とマルチスレッド処理は別の概念
    • 「Unityで非同期処理なんて使えないでしょ」は誤解
    • メインスレッドだけでも非同期処理は書ける
    • Unity開発でも非同期処理は使ってるはず
    • 「コルーチン」はまさに非同期処理の仕組みそのもの
    • 「イベント処理」も非同期処理の仲間

    View full-size slide

  24. async/await
    async/awaitを使ったことない人向けに雑にせつめい

    View full-size slide

  25. async/await
    • 「async」と「await」というキーワードを使った記法
    • C# 5.0で追加された機能
    • 非同期処理を同期処理と似た記法で扱うことができる
    • asyncってメソッドの頭につけると非同期メソッド化する
    • 非同期メソッド内でawaitって書くとそこで処理が一時停止する
    • 裏でコンパイラがステートマシンを作っていい感じにしてくれる

    View full-size slide

  26. async/awaitの使い方
    • Task(またはそれに準ずるオブジェクト)
    と組み合わせて使う
    • 今の御時世ではValueTask, UniTaskの方がよい

    View full-size slide

  27. 「待つ」
    • async/awaitは非同期処理を「待つ」ための機構
    • 並行で実行した処理が終わるのをいい感じに待てる!
    • 簡単な記法で「待つ」ことができる!
    • 非同期処理から結果を取り出すのも簡単!
    • try-catchも使えてエラーハンドリングも完璧!

    View full-size slide

  28. 注意
    • async/awaitはマルチスレッド処理のための機能ではない
    • awaitするとマルチスレッド処理になる、は間違い!
    • async/awaitは「待つ」ための仕組みであって、スレッドの話は関係ない!
    • マルチスレッドの機能がくっついてるのは「Task」の方
    • async/awaitはTaskと組み合わせることが多いので、それで誤解しやすい
    • だいたいTaskが悪い

    View full-size slide

  29. 「async/await」をわかりやすく言うと
    • 強い“コルーチン”
    • いろいろ語弊はあるがUnityからC#を始めた人にはこういうイメージが
    一番伝わりやすいと思う

    View full-size slide

  30. 比較
    • 記法はすごく似てる
    • とりあえず最初はコルーチンの感覚でasync/awaitを書けば良い
    コルーチン async/await

    View full-size slide

  31. async/awaitの書き方解説
    asyncって書くと、
    awaitが使えるようになる

    View full-size slide

  32. async/awaitの書き方解説
    コルーチンと違って戻り値が指定できる!
    await時にそのまま受け取り可能!
    ジェネリック版のTaskなどを指定して
    そのままreturnするだけ

    View full-size slide

  33. async/awaitの書き方解説
    キャンセルにはCancellationTokenを使う

    View full-size slide

  34. コルーチンと比較したasync/await
    コルーチン async/await
    どこで動くか
    Unityのライフサイクル上で実行
    (GameObjectに紐づく)
    実装による
    戻り値 利用できない returnが使える
    キャンセル GameObjectを破棄すれば止まる 明示的に停止する必要あり
    「待てる」対象
    限定的
    (AsynOperation,YieldConstruction など)
    何でも待てる
    (GetAwaiter()さえあれば)
    エラーハンドリング つらい try-catchが使える
    非同期処理の連結 かなりつらい 手続き的に書ける

    View full-size slide

  35. 「async/await」まとめ
    • async/awaitは「待つ」ための機構
    • 非同期処理を気軽に扱うための機構
    • async/awaitはマルチスレッド処理の機能ではない!!!!!
    • Unityでもasync/awaitは便利に使える
    • めっちゃ進化したすごい「コルーチン」と思えばOK
    • Unity上でも普通に使えるし、UniTaskを使えばもっと便利になる

    View full-size slide

  36. ちゃんとした解説
    • ちゃんとしたasync/awaitの解説はこちら
    • さては非同期だなオメー!async/await完全に理解しよう
    • https://learning.unity3d.jp/275/
    • async/await のしくみ
    • https://learning.unity3d.jp/1510/

    View full-size slide

  37. Unityでasync/awaitを快適に使うために
    •「UniTask」
    • Unity上でasync/awaitを快適に不便なく使うためのライブラリ

    View full-size slide

  38. UniTaskとは
    • Unity向けのasync/await拡張ライブラリ
    • Unityにおけるasync/awaitを大幅に強化してくれる
    • MITライセンスで公開中
    • https://github.com/Cysharp/UniTask
    • Releaseからパッケージを入れるなり
    • OpenUPMなどからいれるなり

    View full-size slide

  39. UniTaskの機能
    • Unityにおけるasync/awaitを大幅強化
    • async/awaitのパフォーマンスの向上(アロケーションが徹底的に削減)
    • Unity開発に特化した機能の提供
    • 各種Awaiter実装の提供
    • UnityEditor上でawait状態の可視化(UniTaskTracker)
    • UniTaskAsyncEnumerable / UniTask.LINQ

    View full-size slide

  40. UniTask
    機能紹介

    View full-size slide

  41. UniTaskの機能
    • UniTask / UniTask
    • UniTaskCompletionSource
    • 各種Awaiter
    • ファクトリメソッド
    • その他便利なメソッド
    • UniTaskTracker
    • UniTaskAsyncEnumerable

    View full-size slide

  42. UniTask/UniTask
    いちばん基本的なオブジェクト

    View full-size slide

  43. UniTask / UniTask
    • ValueTask / IValueTaskSource のUnity向け実装
    • ゼロアロケーションで動作する
    • 理由が無い限りTaskはもう使わない
    • 代わりにUniTaskを使おう

    View full-size slide

  44. TaskとUniTaskの違い
    Task UniTask
    機能 Unityでは不要な機能が多い Unityで活用できる機能中心
    オブジェクトサイズ 大きい 小さい
    実行コンテキスト
    SynchronizationContextに
    暗黙的に依存
    明示的に操作しない限り
    現在のコンテキストを保つ
    メモリアロケート 常にヒープを確保する ゼロアロケーション
    await時の制約 なし
    同じUniTaskインスタンスは
    2回awaitできない

    View full-size slide

  45. TaskとUniTaskの違い
    Task UniTask
    機能 Unityでは不要な機能が多い Unityで活用できる機能中心
    オブジェクトサイズ 大きい 小さい
    実行コンテキスト
    SynchronizationContextに
    暗黙的に依存
    明示的に操作しない限り
    現在のコンテキストを保つ
    メモリアロケート 常にヒープを確保する ゼロアロケーション
    await時の制約 なし
    同じUniTaskインスタンスは
    2回awaitできない

    View full-size slide

  46. TaskとUniTaskの違い
    Task UniTask
    機能 Unityでは不要な機能が多い Unityで活用できる機能中心
    オブジェクトサイズ 大きい 小さい
    実行コンテキスト
    SynchronizationContextに
    暗黙的に依存
    明示的に操作しない限り
    現在のコンテキストを保つ
    メモリアロケート 常にヒープを確保する ゼロアロケーション
    await時の制約 なし
    同じUniTaskインスタンスは
    2回awaitできない

    View full-size slide

  47. 制約
    • 同じUniTaskインスタンスは2回以上awaitできない
    • パフォーマンス向上のための仕様
    • 2回awaitすると例外が出る
    • 2回以上awaitしたい場合は
    • Preserve()メソッドを呼ぶ
    • UniTask.Lazy()で包む

    View full-size slide

  48. UniTaskVoid
    • async void の代わりに使うやつ
    • UniTaskの機構の上で動くasync void相当のやつ

    View full-size slide

  49. UniTask/UniTaskの使い方

    View full-size slide

  50. Taskから置換すれば動く
    • Taskって書いてるところをUniTaskに変えれば動く
    • ちょっと書き換えるだけでパフォーマンスが向上する!

    View full-size slide

  51. UniTaskのキャンセル方法
    • 基本はCancellationTokenを使う
    • CancellationTokenを使ってキャンセル命令を送ることができる
    • CancellationTokenは省略せずに可能な限り渡すことが大事
    • 「面倒くさいから省略しちゃおう」は絶対ダメ

    View full-size slide

  52. GetCancellationTokenOnDestroyが便利
    • MonoBehaviour上ならこれが楽
    • Destroyされたら自動的にキャンセルされるCancellationTokenを生成する

    View full-size slide

  53. CancellationTokenSource
    • CancellationTokenSourceを使うと手動で作れる

    View full-size slide

  54. CancellationTokenが渡せない時
    • OperationCanceledExceptionを使う
    • CancellationTokenが相手に渡せないときはこの例外を投げればOK

    View full-size slide

  55. 補足: OperationCanceledException
    • UniTaskをキャンセル状態にすることができる特殊例外
    • この例外が発行されたときのみUniTaskはキャンセル状態になる
    • それ以外の例外の場合は失敗状態になる

    View full-size slide

  56. 補足: UniTaskScheduler
    • UniTaskの未処理例外の管理を行っている機構
    • これはUniTask内で発生した例外のうちOperationCanceledException
    のみ無視してくれる(ログに出さずにもみ消してくれる)
    • UniTaskを使うときはOperationCanceledExceptionはcatchせず
    スルーしておけばOK

    View full-size slide

  57. UniTaskの機能
    • UniTask / UniTask
    • UniTaskCompletionSource
    • 各種Awaiter
    • ファクトリメソッド
    • その他便利なメソッド
    • UniTaskTracker
    • UniTaskAsyncEnumerable

    View full-size slide

  58. UniTaskCompletionSource
    UniTaskを手動でつくる仕組み

    View full-size slide

  59. UniTaskCompletionSource
    • 任意のタイミングで状態を変更できるUniTaskを作る機能
    • UniRx.AsyncSubjectと似てる
    • 生成されたUniTaskは何回でもawaitできる

    View full-size slide

  60. 使用例:初期化完了待ち

    View full-size slide

  61. 使用例:初期化完了待ち

    View full-size slide

  62. 派生:AutoResetUniTaskCompletionSource
    • 内部的にリソースが再利用されるUTCS
    • 使い方はUniTaskCompletationSourceとほぼ同じ
    • インスタンス化の方法がちょっと違う
    • ただしこっちは1回しかawaitできない
    • ライブラリ内でオブジェクトプールされてる
    • デストラクト時に自動的に返却される
    • 使い捨て用途向き

    View full-size slide

  63. 比較
    • UniTaskCompletionSource
    • 何回でもawaitできるUniTaskを生成できる
    • フィールドに定義して使ったりするのによさげ
    • AutoResetUniTaskCompletionSource
    • 自動的に再利用されるため使用コストが低い
    • 生成されたUniTaskは1回しかawaitできない
    • メソッド内など、狭いスコープ内で使い捨てる用途に向いてる

    View full-size slide

  64. UniTaskの機能
    • UniTask / UniTask
    • UniTaskCompletionSource
    • 各種Awaiter
    • ファクトリメソッド
    • その他便利なメソッド
    • UniTaskTracker
    • UniTaskAsyncEnumerable

    View full-size slide

  65. Awaiter
    awaitするときに使うやつ

    View full-size slide

  66. Awaiter
    • オブジェクトをawait可能にする機構
    • あるオブジェクトのGetAwaiter()メソッドからAwaiterが取得できるとき、
    そのオブジェクトはawaitが可能になる
    • UniTaskを導入すると、いろんなオブジェクトにこのAwaiterが追加される
    • つまり、Unityのあらゆるオブジェクトや事象がawaitできるようになる!

    View full-size slide

  67. Awaiter: AsyncOperation
    • コルーチンで待たないといけなかったやつがasync/awaitで待機可能になる

    View full-size slide

  68. Awaiter: AsyncOperation
    • returnでそのまま結果を返せて便利!

    View full-size slide

  69. Awaiter:コルーチン

    View full-size slide

  70. Awaiter: uGUI
    • uGUIのイベントもawaitで待てるようになる
    • awaitが1回でいいなら On***Async()を呼び出す

    View full-size slide

  71. Awaiter: uGUI
    繰り返してawaitする場合は「AsyncHandler」を取得する

    View full-size slide

  72. Awaiter: uGUI
    LINQと組み合わせるならUniTaskAsyncEnumerable化する

    View full-size slide

  73. Awaiter: MonoBehaviour
    MonoBehaviourのすべてのイベントをawaitできる

    View full-size slide

  74. Awaiter: DOTween
    OpenUPMからDOTweenを導入、または「UNITASK_DOTWEEN_SUPPORT」
    を定義している場合に利用可能

    View full-size slide

  75. Awaiter : CancellationToken

    View full-size slide

  76. UniTaskの機能
    • UniTask / UniTask
    • UniTaskCompletionSource
    • 各種Awaiter
    • ファクトリメソッド
    • その他便利なメソッド
    • UniTaskTracker
    • UniTaskAsyncEnumerable

    View full-size slide

  77. ファクトリメソッド
    特殊なUniTaskを生成したりする便利な関数

    View full-size slide

  78. ファクトリメソッド
    • 特殊な動作をするUniTaskを生成する
    • 基本はawaitと組み合わせて使う
    • たくさんある
    • 全部は紹介しません

    View full-size slide

  79. UniTask.Delay / DelayFrame
    • 指定した時間/フレーム数待機できる

    View full-size slide

  80. UniTask.Yield
    • awaitすると実行コンテキストを切り替えることができる
    • すでに同コンテキストにいる場合はそのまま1回待機する

    View full-size slide

  81. 指定可能なタイミング
    • Initialization
    • LastInitialization
    • EarlyUpdate
    • LastEarlyUpdate
    • FixedUpdate
    • LastFixedUpdate
    • PreUpdate
    • LastPreUpdate
    • Update
    • LastUpdate
    • PreLateUpdate
    • LastPreLateUpdate
    • PostLateUpdate
    • LastPostLateUpdate

    View full-size slide

  82. 指定可能なタイミング
    • Initialization
    • LastInitialization
    • EarlyUpdate
    • LastEarlyUpdate
    • FixedUpdate
    • LastFixedUpdate
    • PreUpdate
    • LastPreUpdate
    • Update
    • LastUpdate
    • PreLateUpdate
    • LastPreLateUpdate
    • PostLateUpdate
    • LastPostLateUpdate
    • コルーチンの WaitForEndOfFrame に相当

    View full-size slide

  83. UniTask.NextFrame
    • 確実に次のフレーム(以降)のコンテキストに切り替える
    • Yield()はタイミングによっては同じフレーム間で切り替わることがある
    • こちらは確実に次のフレーム(以降)となる

    View full-size slide

  84. UniTask.SwitchToThreadPool
    • awaitするとコンテキストをスレッドプールに切り替える

    View full-size slide

  85. UniTask.SwitchToMainThread
    • awaitすると実行コンテキストをメインスレッドに戻す
    • 戻り先は PlayerLoopTiming.Update に相当
    • すでにメインスレッドなら何もしない

    View full-size slide

  86. UniTask.WaitUntil / WaitWhile
    • 条件を満たすまで/満たさなくなるまで待つ
    • コルーチンにあったやつ

    View full-size slide

  87. UniTask.WaitUntilCanceled
    • CancellationTokenをUniTask化する
    • キャンセル命令が出ると、UniTaskは完了状態になる
    • キャンセル命令が出たらawaitの次に進む、ができる

    View full-size slide

  88. UniTask.WhenAll
    • 複数のUniTaskがすべて完了するのを待つ

    View full-size slide

  89. UniTask.WhenAll
    • 型が一致してなくてもOK

    View full-size slide

  90. UniTask.WhenAll
    • ValueTupleへのawaitはUniTask.WhenAll扱いになる

    View full-size slide

  91. UniTask.WhenAny
    • 複数のUniTaskのうちどれか1個が終わるのを待つ
    (ちなみに uniTask.Timeoutの実装はこれとだいたい同じ)

    View full-size slide

  92. UniTask.Create
    • UniTaskをラムダ式から作る時に便利

    View full-size slide

  93. UniTask.Defer
    • UniTaskの起動を遅延できる
    • awaitするまでUniTaskの起動を遅らせることができる

    View full-size slide

  94. UniTask.Lazy
    • UniTaskの起動を遅延できる
    • こっちはAsyncLazy型になる(何回でもawaitできるUniTaskになる)

    View full-size slide

  95. それぞれの違い
    • UniTask.Create
    • Createを呼んだ瞬間に新しいUniTaskを生成する
    • UniTask.Defer
    • awaitした瞬間にUniTaskを1回だけ生成する
    • 1回しかawaitできないかわりにLazyより軽量
    • UniTask.Lazy
    • awaitした瞬間にAsyncLazy型にラップしてUniTaskを生成する
    • 生成したUniTaskは何回でもawaitできる

    View full-size slide

  96. それぞれの違い
    • UniTask.Create
    • Createを呼んだ瞬間に新しいUniTaskを生成する
    • UniTask.Defer
    • awaitした瞬間にUniTaskを1回だけ生成する
    • 1回しかawaitできないかわりにLazyより軽量
    • UniTask.Lazy
    • awaitした瞬間にAsyncLazy型にラップしてUniTaskを生成する
    • 生成したUniTaskは何回でもawaitできる

    View full-size slide

  97. UniTask.Void
    • async/awaitをvoid型の関数にラップできる
    • 中に書いたasyncメソッドはForget()される

    View full-size slide

  98. UniTask.Action/UnityAction
    • async/awaitをAction/UnityActionにラップできる

    View full-size slide

  99. UniTaskの機能
    • UniTask / UniTask
    • UniTaskCompletionSource
    • 各種Awaiter
    • ファクトリメソッド
    • その他便利なメソッド
    • UniTaskTracker
    • UniTaskAsyncEnumerable

    View full-size slide

  100. その他便利メソッド

    View full-size slide

  101. Timeout / TimeoutWithoutException
    • UniTaskのawaitにタイムアウトをつける
    • この2つの違いはタイムアウト時に例外が飛ぶ / 飛ばない

    View full-size slide

  102. Forget()
    • UniTaskを投げっぱなし(Fire-and-forget)化する
    • 非同期処理を起動するけどawaitせず放置するときに使う

    View full-size slide

  103. IDisposable.AddTo(CancellationToken)
    • IDisposable.Dispose()呼び出しをCancellationTokenに
    連動させることができる

    View full-size slide

  104. UniTaskの機能
    • UniTask / UniTask
    • UniTaskCompletionSource
    • 各種Awaiter
    • ファクトリメソッド
    • その他便利なメソッド
    • UniTaskTracker
    • UniTaskAsyncEnumerable

    View full-size slide

  105. UniTaskTracker

    View full-size slide

  106. UniTaskTracker
    • Unityエディタ上でUniTaskの待機状況を確認できる
    • 待機状況、経過時間、スタックトレースを確認できる
    • リークしてないかのチェックが簡単にできる

    View full-size slide

  107. UniTaskの機能
    • UniTask / UniTask
    • UniTaskCompletionSource
    • 各種Awaiter
    • ファクトリメソッド
    • その他便利なメソッド
    • UniTaskTracker
    • UniTaskAsyncEnumerable

    View full-size slide

  108. UniTaskAsyncEnumerable

    View full-size slide

  109. UniTaskAsyncEnumerable
    • IAsyncEnumerableのUniTask実装
    • 複数個の非同期処理をasync/awaitベースで扱える仕組み
    • C# 8が使えないUnityバージョンでも動作する
    • 長くなるので一旦区切ります(この後に話す)

    View full-size slide

  110. UniTaskの機能紹介 まとめ
    • Unityでのasync/await生活を便利にする機能満載
    • async/await使うなら無条件で導入するべき
    • UniRxと比べるとまだわかりやすいライブラリ

    View full-size slide

  111. UniTaskAsyncEnumerable
    UniTask2の目玉機能

    View full-size slide

  112. UniTaskAsyncEnumerable
    • IAsyncEnumerableのUniTask版実装

    View full-size slide

  113. UniTaskAsyncEnumerable
    • IAsyncEnumerableのUniTask版実装
    • まずはIAsyncEnumerableを先に理解したほうが話が早い

    View full-size slide

  114. IAsyncEnumerable
    ここからはUnityではなく、純粋なC#の話

    View full-size slide

  115. IAsyncEnumerable
    • C#8.0で正式に追加されたPull型非同期ストリーム
    • IEnumerableの非同期版
    • 基本はawait foreachとセットで使う

    View full-size slide

  116. インタフェース定義

    View full-size slide

  117. IEnumerableとの比較

    View full-size slide

  118. IEnumerableとの比較
    イテレータを返す
    非同期イテレータを返す

    View full-size slide

  119. IEnumeratorの比較

    View full-size slide

  120. IEnumeratorの比較
    次の値が存在するかを非同期で確認できる
    trueなら存在する
    次の値が存在するならtrue

    View full-size slide

  121. 使用例の比較

    View full-size slide

  122. 使用例の比較

    View full-size slide

  123. Foreachの比較

    View full-size slide

  124. Foreachの比較
    2箇所でawaitが使える
    ・次の値を取りに行く時
    ・得た値を使って処理を実行するとき

    View full-size slide

  125. 何を実現するためのものなのか
    • 複数個の非同期処理に対して逐次処理を行う機構
    • 直列実行が得意

    View full-size slide

  126. 「複数個の」非同期処理といえば
    • IObservableがある
    • いわゆるReactive Extensions(Rx)
    • これと何が違うのか?

    View full-size slide

  127. IObservableとの違い
    • IObservableはPush型
    • IAsyncEnumerableはPull型

    View full-size slide

  128. 使い分け
    • IObservable
    • メッセージを多数にブロードキャストするのに向く(イベント駆動向け)
    • 非同期処理の結果を用いて並行実行するのが得意
    • IAsyncEnumerable
    • 単一の消費者がメッセージの流量を制御しながら処理できる
    • 非同期処理の逐次実行がやりやすい

    View full-size slide

  129. UniTaskAsyncEnumerable
    Unityの話にもどる

    View full-size slide

  130. UniTaskAsyncEnumerable
    • IAsyncEnumerableのUniTask版実装
    • インタフェースはIUniTaskAsyncEnumerable
    • 仕組みはIAsyncEnumerableとまったく同じ

    View full-size slide

  131. IAsyncEnumerable相当のことができる!
    • C# 8がまだ使えないUnityでもIAEが使える!
    • await foreach構文が使えない以外は同じことができる
    • 挙動はまったく同じ
    • LINQも使える

    View full-size slide

  132. UniTaskAsyncEnumerableの
    使い方
    await foreachの代替方法

    View full-size slide

  133. await foreachの代わりに
    • ForEachAsync
    • ForEachAwaitAsync
    • Subscribe

    View full-size slide

  134. 例:HTTP通信する

    View full-size slide

  135. ForEachAsync
    • 結果を待ち受けて、同期的に処理を行う
    • メッセージを使った処理を同期的に実行する
    • 処理が終わったら次の値を取りに行く

    View full-size slide

  136. ForEachAsync

    View full-size slide

  137. ForEachAwaitAsync
    • 中でasync/awaitが使えるようになる
    • asyncメソッドが最後まで終わったら次の値を取りに行く

    View full-size slide

  138. ForEachAwaitAsync

    View full-size slide

  139. Subscribe
    • async/awaitが使えるが、Forget()される
    • ForEachAwaitAsyncと記法は似てるけど挙動がぜんぜん違う

    View full-size slide

  140. Subscribe
    この場合は
    「await UniTask.Delay」を
    呼び出す意味がない

    View full-size slide

  141. メッセージの消費パターン
    • ForEachAsync
    • while (await e.MoveNextAsync()) action(e.Current);
    • ForEachAwaitAsync
    • while (await e.MoveNextAsync()) await action(e.Current);
    • Subscribe
    • while (await e.MoveNextAsync()) action(e.Current).Forget();

    View full-size slide

  142. UniTask.LINQ
    IUniTaskAsyncEnumerableの加工ができる

    View full-size slide

  143. UniTask.LINQ
    • UniTaskAsyncEnumerableでもLINQが使える!
    • 非同期シーケンスに対していろいろ加工ができる機能
    • UniRxのオペレータに近い
    • 数が多いのと、基本的にLINQと動作が変わらないので詳細は省略

    View full-size slide

  144. 同期版と非同期版
    • 同期版と非同期版が存在する
    • 同期で動くものと、async/awaitが使えるもの2種類ある
    • 例: Select, SelectAwait
    • 適宜組み合わせて使おう

    View full-size slide

  145. UniTaskAsyncEnumerableの
    作り方

    View full-size slide

  146. 作り方
    • ファクトリメソッド
    • uGUIから変換
    • 他のデータ構造から変換
    • Channel
    • AsyncReactiveProperty

    View full-size slide

  147. ファクトリメソッド
    • 特殊機能なUniTaskAsyncEnumerableを生成できる
    • Return, Empty, Never, Throw, Range, Repeat
    • EveryUpdate
    • Timer/TimerFrame
    • Interval/IntervalFrame
    • EveryValueChanged
    • Observableのファクトリメソッドに似てる

    View full-size slide

  148. 例:UniTaskAsyncEnumerable.EveryUpdate
    • 毎フレームメッセージを発行する

    View full-size slide

  149. 作り方
    • ファクトリメソッド
    • uGUIから変換
    • 他のデータ構造から変換
    • Channel
    • AsyncReactiveProperty

    View full-size slide

  150. uGUIのイベントから変換

    View full-size slide

  151. 作り方
    • ファクトリメソッド
    • uGUIから変換
    • 他のデータ構造から変換
    • Channel
    • AsyncReactiveProperty

    View full-size slide

  152. 他のデータ構造から変換
    • 各種データ構造から変換できる
    • IEnumerable
    • IObservable
    • Task
    • UniTask

    View full-size slide

  153. 例: IObservableからの変換
    • Observableから発行されたメッセージはキューに積まれる
    • メッセージの取りこぼし無しでUniTaskAsyncEnumerable化できる

    View full-size slide

  154. 注意点
    • UniTaskAsyncEnumerableは生成の仕方によっては
    メッセージを取りこぼすことがある
    • タイミングが合わなかったイベントは無視されることがある
    • 生成の仕方によって挙動が違う

    View full-size slide

  155. 取りこぼす例:EveryUpdate
    • ForEachAwaitAsyncを使っていると取りこぼしが起きる

    View full-size slide

  156. 例: 取りこぼすことを逆に利用した実装
    ボタンを押したら非同期処理を走らせて、終わるまでボタン入力を無視

    View full-size slide


  157. • 取りこぼすことがある
    • EveryUpdate()
    • uGUIのイベントから変換
    • AsyncReactiveProperty
    • 取りこぼさない(キュー機構あり)
    • IObservableからの変換
    • Channel

    View full-size slide

  158. 取りこぼしを防ぐには
    • 「Queue()」を使う
    • メッセージがバッファリングされるようになる

    View full-size slide

  159. 作り方
    • ファクトリメソッド
    • uGUIから変換
    • 他のデータ構造から変換
    • Channel
    • AsyncReactiveProperty

    View full-size slide

  160. Channel
    • UniTaskAsyncEnumerableを使ったメッセージング機構
    • メッセージの送信と受信が可能
    • ObservableでいうところのSubjectに相当
    • Queue機能ありなのでメッセージが漏れることはない

    View full-size slide

  161. SingleConsumerUnboundedChannel
    • Channelのデフォルト実装
    • 単一消費者前提の実装
    • 複数箇所でForEachAsync()すると正しく動作しない
    • 複数箇所で使いたいときはPublish()を併用する

    View full-size slide

  162. 例:Publishと併用
    • Channelを使ったAsyncMessageBrokerの実装例
    • neuecc氏がTwitterにサンプル実装貼ってたやつ
    • https://twitter.com/neuecc/status/1262578286493724672

    View full-size slide

  163. 作り方
    • ファクトリメソッド
    • uGUIから変換
    • 他のデータ構造から変換
    • Channel
    • AsyncReactiveProperty

    View full-size slide

  164. AsyncReactiveProperty
    • IUniTaskAsyncEnumerableベースのReactiveProperty
    • 基本的な挙動はUniRx.ReactivePropertyとだいたい同じ
    • ただawaitしたときの挙動が結構違う
    • Queueの機能は無し
    • 最新の値を1個だけ保持はしてくれるが、それより古い値は取得できない
    • 適宜Queueを組み合わせて使おう

    View full-size slide

  165. AsyncReactivePropertyの特徴
    • awaitがしやすい!
    • LINQと組み合わせれば柔軟にawaitができる
    • UniRx版はawaitしようとすると面倒くさかった

    View full-size slide

  166. AsyncReactivePropertyの使い方

    View full-size slide

  167. 補足:UniRx版との比較
    • UniRx版はawaitしにくい
    • Operatorを使うとIObservable扱いになっちゃう
    • CancellationTokenを使おうとするとUniTaskに変換しないといけない

    View full-size slide

  168. 使い分け
    • UniRx.ReactiveProperty
    • メッセージのブロードキャストに使う
    • イベント駆動の起点に
    • uGUI周りをMV(R)Pパターンで組むならこっち
    • AsyncReactiveProperty
    • async/awaitと組み合わせる前提のときに使う
    • 逐次的な非同期処理メインの場合

    View full-size slide

  169. UniTaskAsyncEnumerable まとめ
    • 複数個の非同期処理も扱いやすくなった
    • 非同期処理にUniRx(Observable)を使う必要がなくなった
    • 新しい機構なので使い方を模索する必要あり
    • Event -> UniTaskAsyncEnumerable のあたりが難しい
    • 活用次第ではいろいろできそう…?

    View full-size slide

  170. UniTaskについて
    • Unityでasync/awaitを使うなら絶対入れるべきライブラリ
    • デメリットが皆無!使おう!
    • async/awaitのパフォーマンスの向上
    • Awaiterが便利
    • UniTask Trackerめっちゃ助かる
    • (UniRxと比べれば)学習コストそんなに高くない

    View full-size slide

  171. UniTaskAsyncEnumerableについて
    • IAsyncEnumerableやっぱすごい
    • IAE自体よく考えられた機能だけあって、かなり便利
    • UniRxよりは遥かにわかりやすい(簡単とは言っていない)
    • もとがIEnumerableなので挙動がまだわかりやすい

    View full-size slide

  172. UniRxとの使い分け
    • async/await + UniTask, UniTaskAsyncEnumerable
    • 非同期処理を書く場合はこちらを優先して使うべき
    • 逐次処理で済む場合は間違いなくこっち
    • UniRx(Observable)
    • 単純な非同期処理にUniRxを使うべきではない
    • イベント処理やメッセージのブロードキャスト等にはまだまだ使えるし需
    要もある

    View full-size slide

  173. 非同期処理における優先度
    1. まずasync/await(UniTask)を検討する
    2. それが無理ならUniTaskAsyncEnumerableを検討
    3. それでも無理ならObservableを使う

    View full-size slide

  174. イベント処理については?
    • 基本はUniRxの方が有利
    • Observableがそもそもイベント駆動の仕組みなので
    • ただし他の選択肢を知っておくことも重要
    • async/await, UniTask, UniTaskAsyncEumerableもイベント処理に利用可能
    • UniRxの代替手段として知っておいて損はない
    • 相互変換もできるのでどれか一個に固執せずに組み合わせて使うのも全然アリ
    • 適材適所でそれぞれの弱点をカバーできるように使うとよさげ

    View full-size slide

  175. 最後のまとめ
    • async/awaitはUnityでも全然使えるよ!
    • むしろ無いと困るレベルで常用する
    • UniTaskAsyncEnumerableが便利!
    • 使用例をブログに書こう!(まだ枯れてないから今がチャンス!)
    • UniRxは使いみちを考えよう
    • 単発 or 逐次処理が主となるような非同期処理ではもう使うべきではなさそう
    • イベントメッセージの発行とか、そっち方面でまだまだ使える

    View full-size slide

  176. おわり
    @toRisouP

    View full-size slide

  177. おまけ UniTask ver2
    Ver1 からの変更点の話

    View full-size slide

  178. UniTask ver2
    • 2020年6月、UniTaskに大型アップデートが!
    • UniTask1 → 2への変更点をまとめて解説

    View full-size slide

  179. 破壊的変更
    • 最低Unityバージョン変更
    • 名前空間の変更
    • UniTaskのawait2度漬け禁止
    • ファクトリメソッドの挙動変更
    • AsyncOperation.ConfigureAwait廃止
    • UniTaskの一部コンストラクタ廃止
    • 一部メソッドのシグネチャ変更

    View full-size slide

  180. 最低Unityバージョン
    • 2018.4.13f1が下限になった
    • UniTask1系では2018.1までサポートしてた

    View full-size slide

  181. 名前空間が変更
    • UniRx.Async -> Cysharp.Threading.Tasks
    • もともとUniTaskがUniRxの一部だったときの名残
    • 今回のアプデで完全に決別

    View full-size slide

  182. UniTaskのawait2度漬け禁止
    • 同じUniTaskインスタンスを2回awaitできなくなった
    • パフォーマンス向上のための仕様変更
    • 2回awaitすると例外が出る
    • 2回以上awaitしたい場合はPreserve()メソッドを呼ぶ

    View full-size slide

  183. ファクトリメソッドの挙動変更
    • UniTaskが起動するタイミングが「定義時」に統一
    • UniTask.Delay() などが呼んだ瞬間に実行開始されるようになった

    View full-size slide

  184. AsyncOperation.ConfigureAwait廃止
    • 代わりにWithCancellation() / ToUniTask()を使う
    • あとキャンセル時にAbort()が自動で呼ばれるようにもなった

    View full-size slide

  185. UniTaskのコンストラクタ廃止
    • UniTask(Func> factory) が廃止
    • UniTask.Lazy相当だったやつ
    • 代替としてUniTask.Defer / UniTask.Lazy を使おう

    View full-size slide

  186. 一部メソッドのシグネチャ変更
    • 戻り値の変更
    • UniTask.Lazy : UniTask -> AsyncLazy
    • UniTask.WhenAny : 組み合わせが変更
    • UniTask.DelayFrame : UniTask -> UniTask
    • 引数の変更
    • IObservable.ToUniTask()
    useFirstValueとCancellationTokenの順序が逆に

    View full-size slide

  187. 追加機能
    • UniTaskAsyncEnumerable + LINQ
    • UniTask ver2の目玉機能

    View full-size slide

  188. UniTask ver2 まとめ
    • 全体的にパフォーマンスが向上
    • ゼロアロケーションで動作する
    • ノーコストでasync/awaitが使える!
    • 破壊的変更に注意
    • awaitが2回できなくなった
    • 一部メソッドのシグネチャが変わってる

    View full-size slide