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

    スレッドがどうこうとかいう話は本来は関係ない • マルチスレッド処理と非同期処理の相性がいいのは事実だけど、概念としては別物 • なんならシングルスレッドのみを使っても非同期処理は実現できる
  2. コルーチンと比較したasync/await コルーチン async/await どこで動くか Unityのライフサイクル上で実行 (GameObjectに紐づく) 実装による 戻り値 利用できない returnが使える

    キャンセル GameObjectを破棄すれば止まる 明示的に停止する必要あり 「待てる」対象 限定的 (AsynOperation,YieldConstruction など) 何でも待てる (GetAwaiter()さえあれば) エラーハンドリング つらい try-catchが使える 非同期処理の連結 かなりつらい 手続き的に書ける
  3. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  4. UniTask / UniTask<T> • ValueTask / IValueTaskSource のUnity向け実装 • ゼロアロケーションで動作する

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

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

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

    SynchronizationContextに 暗黙的に依存 明示的に操作しない限り 現在のコンテキストを保つ メモリアロケート 常にヒープを確保する ゼロアロケーション await時の制約 なし 同じUniTaskインスタンスは 2回awaitできない
  8. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  9. 比較 • UniTaskCompletionSource • 何回でもawaitできるUniTaskを生成できる • フィールドに定義して使ったりするのによさげ • AutoResetUniTaskCompletionSource •

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

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

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  12. 指定可能なタイミング • Initialization • LastInitialization • EarlyUpdate • LastEarlyUpdate •

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

    FixedUpdate • LastFixedUpdate • PreUpdate • LastPreUpdate • Update • LastUpdate • PreLateUpdate • LastPreLateUpdate • PostLateUpdate • LastPostLateUpdate • コルーチンの WaitForEndOfFrame に相当
  14. それぞれの違い • UniTask.Create • Createを呼んだ瞬間に新しいUniTaskを生成する • UniTask.Defer • awaitした瞬間にUniTaskを1回だけ生成する •

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

    1回しかawaitできないかわりにLazyより軽量 • UniTask.Lazy • awaitした瞬間にAsyncLazy型にラップしてUniTaskを生成する • 生成したUniTaskは何回でもawaitできる
  16. UniTaskの機能 • UniTask / UniTask<T> • UniTaskCompletionSource • 各種Awaiter •

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

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

    ファクトリメソッド • その他便利なメソッド • UniTaskTracker • UniTaskAsyncEnumerable
  19. メッセージの消費パターン • 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();
  20. ファクトリメソッド • 特殊機能なUniTaskAsyncEnumerableを生成できる • Return, Empty, Never, Throw, Range, Repeat

    • EveryUpdate • Timer/TimerFrame • Interval/IntervalFrame • EveryValueChanged • Observableのファクトリメソッドに似てる
  21. 例 • 取りこぼすことがある • EveryUpdate() • uGUIのイベントから変換 • AsyncReactiveProperty •

    取りこぼさない(キュー機構あり) • IObservable<T>からの変換 • Channel
  22. 使い分け • UniRx.ReactiveProperty • メッセージのブロードキャストに使う • イベント駆動の起点に • uGUI周りをMV(R)Pパターンで組むならこっち •

    AsyncReactiveProperty • async/awaitと組み合わせる前提のときに使う • 逐次的な非同期処理メインの場合
  23. UniRxとの使い分け • async/await + UniTask, UniTaskAsyncEnumerable • 非同期処理を書く場合はこちらを優先して使うべき • 逐次処理で済む場合は間違いなくこっち

    • UniRx(Observable) • 単純な非同期処理にUniRxを使うべきではない • イベント処理やメッセージのブロードキャスト等にはまだまだ使えるし需 要もある
  24. イベント処理については? • 基本はUniRxの方が有利 • Observableがそもそもイベント駆動の仕組みなので • ただし他の選択肢を知っておくことも重要 • async/await, UniTask,

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

    UniRxは使いみちを考えよう • 単発 or 逐次処理が主となるような非同期処理ではもう使うべきではなさそう • イベントメッセージの発行とか、そっち方面でまだまだ使える
  26. 破壊的変更 • 最低Unityバージョン変更 • 名前空間の変更 • UniTaskのawait2度漬け禁止 • ファクトリメソッドの挙動変更 •

    AsyncOperation.ConfigureAwait廃止 • UniTaskの一部コンストラクタ廃止 • 一部メソッドのシグネチャ変更
  27. 一部メソッドのシグネチャ変更 • 戻り値の変更 • UniTask.Lazy : UniTask -> AsyncLazy •

    UniTask.WhenAny : 組み合わせが変更 • UniTask.DelayFrame : UniTask<int> -> UniTask • 引数の変更 • IObservable<T>.ToUniTask() useFirstValueとCancellationTokenの順序が逆に
  28. UniTask ver2 まとめ • 全体的にパフォーマンスが向上 • ゼロアロケーションで動作する • ノーコストでasync/awaitが使える! •

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