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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. 非同期処理
    #とは

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  13. 非同期処理

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  38. UniTask

    View Slide

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

    View Slide

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

    View Slide

  41. UniTask
    機能紹介

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  50. UniTask/UniTaskの使い方

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide


  58. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  71. Awaiter:コルーチン

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  77. Awaiter : CancellationToken

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  102. その他便利メソッド

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  107. UniTaskTracker

    View Slide

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

    View Slide

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

    View Slide

  110. UniTaskAsyncEnumerable

    View Slide

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

    View Slide

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

    View Slide

  113. UniTaskAsyncEnumerable
    UniTask2の目玉機能

    View Slide

  114. UniTaskAsyncEnumerable
    • IAsyncEnumerableのUniTask版実装

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  118. インタフェース定義

    View Slide

  119. IEnumerableとの比較

    View Slide

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

    View Slide

  121. IEnumeratorの比較

    View Slide

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

    View Slide

  123. 使用例の比較

    View Slide

  124. 使用例の比較

    View Slide

  125. Foreachの比較

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  130. 比較

    View Slide

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

    View Slide

  132. UniTaskAsyncEnumerable
    Unityの話にもどる

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  137. 例:HTTP通信する

    View Slide

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

    View Slide

  139. ForEachAsync

    View Slide

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

    View Slide

  141. ForEachAwaitAsync

    View Slide

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

    View Slide

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

    View Slide

  144. メッセージの消費パターン
    • 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

  148. UniTaskAsyncEnumerableの
    作り方

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide


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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  164. 例: Write

    View Slide

  165. 例: Read

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  171. AsyncReactivePropertyの使い方

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  175. まとめ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  182. おわり
    @toRisouP

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide