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

E97091ac0d2c69ce9f9e8a8d0a2ea066?s=128

torisoup

June 26, 2020
Tweet

More Decks by torisoup

Other Decks in Technology

Transcript

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

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

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

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

  5. 以前解説した資料 • UniTaskについて説明した資料 • UniTask入門 • https://learning.unity3d.jp/2974/ • UniTask2が出ていろいろ変わった部分がある •

    その部分も含めてアップデートしてます
  6. もくじ • 「非同期処理」 • 「async/await」 • 「UniTask」 • 「UniTaskAsyncEnumerable」

  7. 非同期処理 #とは

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

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

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

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

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

  13. 非同期処理

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

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

  16. よくある勘違い • 非同期処理 = マルチスレッド処理 • これは間違い • 非同期処理は「完了を待たずに呼び出し元にもどる処理」のこと •

    スレッドがどうこうとかいう話は本来は関係ない • マルチスレッド処理と非同期処理の相性がいいのは事実だけど、概念としては別物 • なんならシングルスレッドのみを使っても非同期処理は実現できる
  17. 例:コルーチン • コルーチン • Unityのメインスレッドを使った非同期処理の機構 • メインスレッドのみを使って非同期処理を書くこともできる

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

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

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

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

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

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

    • 「コルーチン」はまさに非同期処理の仕組みそのもの • 「イベント処理」も非同期処理の仲間
  24. async/await async/awaitを使ったことない人向けに雑にせつめい

  25. async/await • 「async」と「await」というキーワードを使った記法 • C# 5.0で追加された機能 • 非同期処理を同期処理と似た記法で扱うことができる • asyncってメソッドの頭につけると非同期メソッド化する

    • 非同期メソッド内でawaitって書くとそこで処理が一時停止する • 裏でコンパイラがステートマシンを作っていい感じにしてくれる
  26. async/awaitの使い方 • Task(またはそれに準ずるオブジェクト) と組み合わせて使う • 今の御時世ではValueTask, UniTaskの方がよい

  27. 「待つ」 • async/awaitは非同期処理を「待つ」ための機構 • 並行で実行した処理が終わるのをいい感じに待てる! • 簡単な記法で「待つ」ことができる! • 非同期処理から結果を取り出すのも簡単! •

    try-catchも使えてエラーハンドリングも完璧!
  28. 注意 • async/awaitはマルチスレッド処理のための機能ではない • awaitするとマルチスレッド処理になる、は間違い! • async/awaitは「待つ」ための仕組みであって、スレッドの話は関係ない! • マルチスレッドの機能がくっついてるのは「Task」の方 •

    async/awaitはTaskと組み合わせることが多いので、それで誤解しやすい • だいたいTaskが悪い
  29. 「async/await」をわかりやすく言うと • 強い“コルーチン” • いろいろ語弊はあるがUnityからC#を始めた人にはこういうイメージが 一番伝わりやすいと思う

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

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

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

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

  34. コルーチンと比較したasync/await コルーチン async/await どこで動くか Unityのライフサイクル上で実行 (GameObjectに紐づく) 実装による 戻り値 利用できない returnが使える

    キャンセル GameObjectを破棄すれば止まる 明示的に停止する必要あり 「待てる」対象 限定的 (AsynOperation,YieldConstruction など) 何でも待てる (GetAwaiter()さえあれば) エラーハンドリング つらい try-catchが使える 非同期処理の連結 かなりつらい 手続き的に書ける
  35. 「async/await」まとめ • async/awaitは「待つ」ための機構 • 非同期処理を気軽に扱うための機構 • async/awaitはマルチスレッド処理の機能ではない!!!!! • Unityでもasync/awaitは便利に使える •

    めっちゃ進化したすごい「コルーチン」と思えばOK • Unity上でも普通に使えるし、UniTaskを使えばもっと便利になる
  36. ちゃんとした解説 • ちゃんとしたasync/awaitの解説はこちら • さては非同期だなオメー!async/await完全に理解しよう • https://learning.unity3d.jp/275/ • async/await のしくみ

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

  38. UniTask

  39. UniTaskとは • Unity向けのasync/await拡張ライブラリ • Unityにおけるasync/awaitを大幅に強化してくれる • MITライセンスで公開中 • https://github.com/Cysharp/UniTask •

    Releaseからパッケージを入れるなり • OpenUPMなどからいれるなり
  40. UniTaskの機能 • Unityにおけるasync/awaitを大幅強化 • async/awaitのパフォーマンスの向上(アロケーションが徹底的に削減) • Unity開発に特化した機能の提供 • 各種Awaiter実装の提供 •

    UnityEditor上でawait状態の可視化(UniTaskTracker) • UniTaskAsyncEnumerable / UniTask.LINQ
  41. UniTask 機能紹介

  42. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  43. UniTask/UniTask<T> いちばん基本的なオブジェクト

  44. UniTask / UniTask<T> • ValueTask / IValueTaskSource のUnity向け実装 • ゼロアロケーションで動作する

    • 理由が無い限りTaskはもう使わない • 代わりにUniTaskを使おう
  45. TaskとUniTaskの違い Task UniTask 機能 Unityでは不要な機能が多い Unityで活用できる機能中心 オブジェクトサイズ 大きい 小さい 実行コンテキスト

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

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

    SynchronizationContextに 暗黙的に依存 明示的に操作しない限り 現在のコンテキストを保つ メモリアロケート 常にヒープを確保する ゼロアロケーション await時の制約 なし 同じUniTaskインスタンスは 2回awaitできない
  48. 制約 • 同じUniTaskインスタンスは2回以上awaitできない • パフォーマンス向上のための仕様 • 2回awaitすると例外が出る • 2回以上awaitしたい場合は •

    Preserve()メソッドを呼ぶ • UniTask.Lazy()で包む
  49. UniTaskVoid • async void の代わりに使うやつ • UniTaskの機構の上で動くasync void相当のやつ

  50. UniTask/UniTask<T>の使い方

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

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

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

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

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

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

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

  58. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  59. UniTaskCompletionSource UniTaskを手動でつくる仕組み

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

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

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

  63. 派生:AutoResetUniTaskCompletionSource • 内部的にリソースが再利用されるUTCS • 使い方はUniTaskCompletationSourceとほぼ同じ • インスタンス化の方法がちょっと違う • ただしこっちは1回しかawaitできない •

    ライブラリ内でオブジェクトプールされてる • デストラクト時に自動的に返却される • 使い捨て用途向き
  64. 比較 • UniTaskCompletionSource • 何回でもawaitできるUniTaskを生成できる • フィールドに定義して使ったりするのによさげ • AutoResetUniTaskCompletionSource •

    自動的に再利用されるため使用コストが低い • 生成されたUniTaskは1回しかawaitできない • メソッド内など、狭いスコープ内で使い捨てる用途に向いてる
  65. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  66. Awaiter awaitするときに使うやつ

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

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

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

  70. Awaiter:コルーチン

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

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

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

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

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

  76. Awaiter : CancellationToken

  77. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  78. ファクトリメソッド 特殊なUniTaskを生成したりする便利な関数

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

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

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

  82. 指定可能なタイミング • Initialization • LastInitialization • EarlyUpdate • LastEarlyUpdate •

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

    FixedUpdate • LastFixedUpdate • PreUpdate • LastPreUpdate • Update • LastUpdate • PreLateUpdate • LastPreLateUpdate • PostLateUpdate • LastPostLateUpdate • コルーチンの WaitForEndOfFrame に相当
  84. UniTask.NextFrame • 確実に次のフレーム(以降)のコンテキストに切り替える • Yield()はタイミングによっては同じフレーム間で切り替わることがある • こちらは確実に次のフレーム(以降)となる

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

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

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

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

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

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

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

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

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

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

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

  96. それぞれの違い • UniTask.Create • Createを呼んだ瞬間に新しいUniTaskを生成する • UniTask.Defer • awaitした瞬間にUniTaskを1回だけ生成する •

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

    1回しかawaitできないかわりにLazyより軽量 • UniTask.Lazy • awaitした瞬間にAsyncLazy型にラップしてUniTaskを生成する • 生成したUniTaskは何回でもawaitできる
  98. UniTask.Void • async/awaitをvoid型の関数にラップできる • 中に書いたasyncメソッドはForget()される

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

  100. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  101. その他便利メソッド

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

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

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

  105. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  106. UniTaskTracker

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

  108. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  109. UniTaskAsyncEnumerable

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

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

  112. UniTaskAsyncEnumerable UniTask2の目玉機能

  113. UniTaskAsyncEnumerable • IAsyncEnumerable<T>のUniTask版実装

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

  115. IAsyncEnumerable<T> ここからはUnityではなく、純粋なC#の話

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

  117. インタフェース定義

  118. IEnumerable<T>との比較

  119. IEnumerable<T>との比較 イテレータを返す 非同期イテレータを返す

  120. IEnumerator<T>の比較

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

  122. 使用例の比較

  123. 使用例の比較

  124. Foreachの比較

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

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

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

  128. IObservable<T>との違い • IObservable<T>はPush型 • IAsyncEnumerable<T>はPull型

  129. 比較

  130. 使い分け • IObservable<T> • メッセージを多数にブロードキャストするのに向く(イベント駆動向け) • 非同期処理の結果を用いて並行実行するのが得意 • IAsyncEnumerable<T> •

    単一の消費者がメッセージの流量を制御しながら処理できる • 非同期処理の逐次実行がやりやすい
  131. UniTaskAsyncEnumerable Unityの話にもどる

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

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

    LINQも使える
  134. UniTaskAsyncEnumerableの 使い方 await foreachの代替方法

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

  136. 例:HTTP通信する

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

  138. ForEachAsync

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

  140. ForEachAwaitAsync

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

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

  143. メッセージの消費パターン • 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();
  144. UniTask.LINQ IUniTaskAsyncEnumerableの加工ができる

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

  146. 同期版と非同期版 • 同期版と非同期版が存在する • 同期で動くものと、async/awaitが使えるもの2種類ある • 例: Select, SelectAwait •

    適宜組み合わせて使おう
  147. UniTaskAsyncEnumerableの 作り方

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

    AsyncReactiveProperty
  149. ファクトリメソッド • 特殊機能なUniTaskAsyncEnumerableを生成できる • Return, Empty, Never, Throw, Range, Repeat

    • EveryUpdate • Timer/TimerFrame • Interval/IntervalFrame • EveryValueChanged • Observableのファクトリメソッドに似てる
  150. 例:UniTaskAsyncEnumerable.EveryUpdate • 毎フレームメッセージを発行する

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

    AsyncReactiveProperty
  152. uGUIのイベントから変換

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

    AsyncReactiveProperty
  154. 他のデータ構造から変換 • 各種データ構造から変換できる • IEnumerable<T> • IObservable<T> • Task<T> •

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

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

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

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

  159. 例 • 取りこぼすことがある • EveryUpdate() • uGUIのイベントから変換 • AsyncReactiveProperty •

    取りこぼさない(キュー機構あり) • IObservable<T>からの変換 • Channel
  160. 取りこぼしを防ぐには • 「Queue()」を使う • メッセージがバッファリングされるようになる

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

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

  163. 例: Write

  164. 例: Read

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

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

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

    AsyncReactiveProperty
  168. AsyncReactiveProperty • IUniTaskAsyncEnumerable<T>ベースのReactiveProperty • 基本的な挙動はUniRx.ReactivePropertyとだいたい同じ • ただawaitしたときの挙動が結構違う • Queueの機能は無し •

    最新の値を1個だけ保持はしてくれるが、それより古い値は取得できない • 適宜Queueを組み合わせて使おう
  169. AsyncReactivePropertyの特徴 • awaitがしやすい! • LINQと組み合わせれば柔軟にawaitができる • UniRx版はawaitしようとすると面倒くさかった

  170. AsyncReactivePropertyの使い方

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

  172. 使い分け • UniRx.ReactiveProperty • メッセージのブロードキャストに使う • イベント駆動の起点に • uGUI周りをMV(R)Pパターンで組むならこっち •

    AsyncReactiveProperty • async/awaitと組み合わせる前提のときに使う • 逐次的な非同期処理メインの場合
  173. UniTaskAsyncEnumerable まとめ • 複数個の非同期処理も扱いやすくなった • 非同期処理にUniRx(Observable)を使う必要がなくなった • 新しい機構なので使い方を模索する必要あり • Event

    -> UniTaskAsyncEnumerable のあたりが難しい • 活用次第ではいろいろできそう…?
  174. まとめ

  175. UniTaskについて • Unityでasync/awaitを使うなら絶対入れるべきライブラリ • デメリットが皆無!使おう! • async/awaitのパフォーマンスの向上 • Awaiterが便利 •

    UniTask Trackerめっちゃ助かる • (UniRxと比べれば)学習コストそんなに高くない
  176. UniTaskAsyncEnumerableについて • IAsyncEnumerable<T>やっぱすごい • IAE<T>自体よく考えられた機能だけあって、かなり便利 • UniRxよりは遥かにわかりやすい(簡単とは言っていない) • もとがIEnumerable<T>なので挙動がまだわかりやすい

  177. UniRxとの使い分け • async/await + UniTask, UniTaskAsyncEnumerable • 非同期処理を書く場合はこちらを優先して使うべき • 逐次処理で済む場合は間違いなくこっち

    • UniRx(Observable) • 単純な非同期処理にUniRxを使うべきではない • イベント処理やメッセージのブロードキャスト等にはまだまだ使えるし需 要もある
  178. 非同期処理における優先度 1. まずasync/await(UniTask)を検討する 2. それが無理ならUniTaskAsyncEnumerableを検討 3. それでも無理ならObservableを使う

  179. イベント処理については? • 基本はUniRxの方が有利 • Observableがそもそもイベント駆動の仕組みなので • ただし他の選択肢を知っておくことも重要 • async/await, UniTask,

    UniTaskAsyncEumerableもイベント処理に利用可能 • UniRxの代替手段として知っておいて損はない • 相互変換もできるのでどれか一個に固執せずに組み合わせて使うのも全然アリ • 適材適所でそれぞれの弱点をカバーできるように使うとよさげ
  180. 最後のまとめ • async/awaitはUnityでも全然使えるよ! • むしろ無いと困るレベルで常用する • UniTaskAsyncEnumerableが便利! • 使用例をブログに書こう!(まだ枯れてないから今がチャンス!) •

    UniRxは使いみちを考えよう • 単発 or 逐次処理が主となるような非同期処理ではもう使うべきではなさそう • イベントメッセージの発行とか、そっち方面でまだまだ使える
  181. おわり @toRisouP

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

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

  184. 破壊的変更 • 最低Unityバージョン変更 • 名前空間の変更 • UniTaskのawait2度漬け禁止 • ファクトリメソッドの挙動変更 •

    AsyncOperation.ConfigureAwait廃止 • UniTaskの一部コンストラクタ廃止 • 一部メソッドのシグネチャ変更
  185. 最低Unityバージョン • 2018.4.13f1が下限になった • UniTask1系では2018.1までサポートしてた

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

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

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

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

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

    UniTask.Lazy を使おう
  191. 一部メソッドのシグネチャ変更 • 戻り値の変更 • UniTask.Lazy : UniTask -> AsyncLazy •

    UniTask.WhenAny : 組み合わせが変更 • UniTask.DelayFrame : UniTask<int> -> UniTask • 引数の変更 • IObservable<T>.ToUniTask() useFirstValueとCancellationTokenの順序が逆に
  192. 追加機能 • UniTaskAsyncEnumerable + LINQ • UniTask ver2の目玉機能

  193. UniTask ver2 まとめ • 全体的にパフォーマンスが向上 • ゼロアロケーションで動作する • ノーコストでasync/awaitが使える! •

    破壊的変更に注意 • awaitが2回できなくなった • 一部メソッドのシグネチャが変わってる