Slide 1

Slide 1 text

UniTask⼊⾨ 2019/07/20 とりすーぷ

Slide 2

Slide 2 text

⾃⼰紹介 • とりすーぷ • @toRisouP • バーチャルキャスト社 • Unityエンジニア • 最近はサーバ開発 • MagicOnion, k8s • Microsoft MVP 2018〜 • Developer Technology

Slide 3

Slide 3 text

今回の内容「UniTask」 • UniTaskの概要 • UniTaskの使い⽅ • 機能紹介 • キャンセル管理について

Slide 4

Slide 4 text

UniTaskの概要

Slide 5

Slide 5 text

UniTaskとは •Unity向けasync/await拡張ライブラリ • Unityの標準機能ではない、⾃分で導⼊が必要な外部ライブラリ • 株式会社Cysharpさんが提供 • 主開発はneuecc⽒(UniRxの作者) • 前はUniRxの⼀部だったが、後に分離したライブラリ化された • その名残で名前空間が「UniRx.Async」のまま

Slide 6

Slide 6 text

UniTaskでできること • 「async/awaitでなんでもできるようになる」 • コルーチンをasync/awaitに置き換え • Unityイベント関数のawait • UnityEditor上でawait状況の可視化

Slide 7

Slide 7 text

例:コルーチンの置き換え

Slide 8

Slide 8 text

例:コルーチンの置き換え コルーチンから結果を取りだすためのデリゲート

Slide 9

Slide 9 text

例:コルーチン -> UniTask

Slide 10

Slide 10 text

例:コルーチン -> UniTask

Slide 11

Slide 11 text

例:コルーチン -> UniTask 結果はそのままreturnすればOK 例外もthrowするだけ

Slide 12

Slide 12 text

UniTaskを使うメリット • コルーチンを使う必要がなくなる • より使いやすいasync/awaitですべてを記述できる • 既存のTaskよりもハイパフォーマンス • Unityに最適化されたTask-likeな機構が使える • UniRxよりはわかりやすい • ⼿続き処理で書ける、は⼗分メリット

Slide 13

Slide 13 text

導⼊⽅法 • GitHubからunitypackageをダウンロードしてインポート • https://github.com/Cysharp/UniTask/releases • AssetStoreにはまだ公開されていない

Slide 14

Slide 14 text

UniTaskが使える条件 • C# 7以降が使えるUnityであること • Unity 2018.3 以降 • Unity 2018.2 以前はIncremental Compilerを導⼊していると使える

Slide 15

Slide 15 text

UniTaskの機能紹介

Slide 16

Slide 16 text

UniTaskが提供する機能 • UniTask型、UniTask型 • UniTaskに付随するstaticメソッド群 • Unity機能のAwaiter • UniTaskTracker

Slide 17

Slide 17 text

UniTaskが提供する機能 • UniTask型、UniTask型 • UniTaskに付随するstaticメソッド群 • Unity機能のAwaiter • UniTaskTracker

Slide 18

Slide 18 text

UniTask型/UniTask型 UniTaskの機能紹介

Slide 19

Slide 19 text

UniTask型/UniTask型 • Unity⽤に最適化されたTask-likeオブジェクト • Task/TaskのUnity向け実装 • Taskの基本機能はだいたい使える

Slide 20

Slide 20 text

Task型 vs UniTask型 Task UniTask 機能 Unityでは不要な機能が多い Unityで活⽤できる機能のみ オブジェクトサイズ ムダに⼤きい ⼩さい 実⾏コンテキスト管理 TaskScheduler & SynchronizationContext PlayerLoop 必要なC# version C# 5.0以上 C# 7.0以上 Task Tracker 無 Unity Editor上で利⽤可能

Slide 21

Slide 21 text

UniTask型の作り⽅ 作り⽅は主に3パターン ・async/awaitの戻り値として使う ・UniTaskCompletionSourceから作る ・Observableから変換する

Slide 22

Slide 22 text

UniTask型の作り⽅ 作り⽅は主に3パターン ・async/awaitの戻り値として使う ・UniTaskCompletionSourceから作る ・Observableから変換する

Slide 23

Slide 23 text

例: async/awaitから作る • async/awaitの戻り値をUniTaskに書き換えるだけでOK • これだけでTask型からUniTask型に変換される

Slide 24

Slide 24 text

例: async/awaitから作る • ジェネリック版のときも同様 • Task を UniTask に置き換えるだけ

Slide 25

Slide 25 text

(補⾜) UniTaskVoid型 • UniTaskの機構に乗った ”async void” • 投げっぱなし(Fire-and-forget)の⾮同期処理として使う

Slide 26

Slide 26 text

UniTask型の作り⽅ 作り⽅は主に3パターン ・async/awaitの戻り値として使う ・UniTaskCompletionSourceから作る ・Observableから変換する

Slide 27

Slide 27 text

UniTaskCompletionSource • ⼿続き処理でUniTaskを⽣成するときに使う • UniRxのAsyncSubjectとだいたい同じ

Slide 28

Slide 28 text

UniTaskCompletionSource

Slide 29

Slide 29 text

UniTask型の作り⽅ 作り⽅は主に3パターン ・async/awaitの戻り値として使う ・UniTaskCompletionSourceから作る ・Observableから変換する

Slide 30

Slide 30 text

Observableからの変換 • UniTaskとObservable(UniRx)は相互変換可能

Slide 31

Slide 31 text

注意点1 • Observableが必ず完了する状態にすること • useFirstValue=true でTake(1)と同等になる

Slide 32

Slide 32 text

注意点2 • UniTask -> Observable 時、 実⾏コンテキストは必ずメインスレッドになる

Slide 33

Slide 33 text

UniTask/UniTask型 まとめ • 使い勝⼿は標準Taskとほとんど同じ • 既存のasyncメソッドを機械的に置換して問題ない • Observableが絡む時だけちょっと注意が必要

Slide 34

Slide 34 text

UniTaskが提供する機能 • UniTask型、UniTask型 • UniTaskに付随するstaticメソッド群 • Unity機能のAwaiter • UniTaskTracker

Slide 35

Slide 35 text

Staticメソッド群 UniTaskの機能紹介

Slide 36

Slide 36 text

UniTaskに付随するstaticメソッド群 • 特殊な挙動をするUniTaskを⽣成できるメソッド群 • FactoryMethod的なやつ • ほとんどがawaitとの併⽤がメイン

Slide 37

Slide 37 text

UniTask.Run • デリゲートをThreadPool上で実⾏する • Task.Runと挙動はおなじ • await後はUnityメインスレッドに戻る(configureAwait = true の時)

Slide 38

Slide 38 text

UniTask.Delay • 指定秒数後に完了するUniTaskを⽣成する • awaitすることで「⼀定時間待つ」ができる • どのタイミングで時間計測するか選べる • デフォルトはUpdate()タイミングで計測

Slide 39

Slide 39 text

UniTask.DelayFrame • 指定フレーム後に完了するUniTaskを⽣成する • awaitすることで「⼀定フレーム待つ」ができる • どのタイミングで計測するか選べる • デフォルトはUpdate()タイミング

Slide 40

Slide 40 text

UniTask.Yield() • 指定のタイミングで1フレーム待機する • 処理をメインスレッドに戻すことにも使える • 引数なしの場合はUpdateタイミングで1フレーム待機になる

Slide 41

Slide 41 text

UniTask.Yield

Slide 42

Slide 42 text

UniTask.SwitchToThreadPool • 実⾏コンテキストをThreadPoolに切り替える • awaitするとそれ以降がThreadPool上での処理になる • 似たものでSwitchToTaskPoolがあるが、こっちは⾮推奨

Slide 43

Slide 43 text

UniTask.SwitchToThreadPool

Slide 44

Slide 44 text

UniTask.SwitchToMainThread • 実⾏コンテキストをメインスレッドに切り替える • awaitするとそれ以降がメインスレッド上での処理になる • UniTask.Yield(PlayerLoopTiming.Update)と微妙に挙動が異なる

Slide 45

Slide 45 text

UniTask.WaitUntil / UniTask.WaitWhile • 条件を満たすまで/満たさなくなるまで待機する • コルーチンのWaitUntil/WaitWhileと同等 • どのPlayerLoopタイミングでチェックするかも指定できる

Slide 46

Slide 46 text

UniTask.WaitUntil / UniTask.WaitWhile

Slide 47

Slide 47 text

UniTask.WaitUntilValueChanged • 指定のオブジェクトのパラメータが変動するまで待機する • UniRxのObserveEveryValueChangedと同じ • 対象は弱参照で保持され、GCされるとawaitはキャンセルされる

Slide 48

Slide 48 text

UniTask.WhenAll • 指定のUniTaskがすべて完了するまで待機する • UniTaskの型がすべて⼀致していなくてもOK

Slide 49

Slide 49 text

UniTask.WhenAll

Slide 50

Slide 50 text

UniTask.WhenAny • 複数のUniTaskのうちどれか1つが先に完了するまで待機する

Slide 51

Slide 51 text

Staticメソッド群 まとめ • Unity向けに調整された機能が多くて便利 • 標準Taskとくらべて使いやすくなってたりもする • まだまだあるけど量が多いので省略

Slide 52

Slide 52 text

UniTaskが提供する機能 • UniTask型、UniTask型 • UniTaskに付随するstaticメソッド群 • Unity機能のAwaiter • UniTaskTracker

Slide 53

Slide 53 text

Awaiter UniTaskの機能紹介

Slide 54

Slide 54 text

Awaiterって何 • オブジェクトのawaitに必要なヤツ • あるオブジェクトにGetAwaiter()メソッドが実装されており、 そこからAwaiterが取得できるならそのオブジェクトはawaitができる

Slide 55

Slide 55 text

補⾜:GetAwaiter() • Awaiterを取得するためのメソッド • クラスメソッドだけでなく、拡張メソッドでもOK • ⾃前で実装すればなんでもawaitできるようになる awaitにカーソルを合わせて定義元を参照するとどのGetAwaiter()が呼ばれるかわかる

Slide 56

Slide 56 text

UniTaskとAwaiter • UniTaskはUnityのあらゆるオブジェクトの Awaiterを提供してくれる

Slide 57

Slide 57 text

UniTaskが提供するAwaiter • コルーチン • AsyncOperation • uGUI Event • Unityコールバック • JobHandle • UnityEvent

Slide 58

Slide 58 text

コルーチンのAwaiter • IEnumeratorをawaitするとコルーチンとして実⾏&待機する

Slide 59

Slide 59 text

コルーチンのAwaiter

Slide 60

Slide 60 text

(補⾜)コルーチン上でUniTask • ToCoroutine()でUniTaskをコルーチン化もできる

Slide 61

Slide 61 text

AsyncOperationのAwaiter

Slide 62

Slide 62 text

AsyncOperationのAwaiter • ConfigureAwaitで進⾏状況取得可能

Slide 63

Slide 63 text

uGUI EventのAwaiter ※using UniRx.Async.Triggers が必要

Slide 64

Slide 64 text

UnityコールバックのAwaiter • MonoBehaviourに⾶んでくるイベントコールバックのこと ※using UniRx.Async.Triggers が必要

Slide 65

Slide 65 text

Awaiter まとめ • Unityのあらゆる要素がawait可能になる • コルーチンの上位互換としてasync/awaitが利⽤可能になる

Slide 66

Slide 66 text

UniTaskが提供する機能 • UniTask型、UniTask型 • UniTaskに付随するstaticメソッド群 • Unity機能のAwaiter • UniTaskTracker

Slide 67

Slide 67 text

UniTaskTracker UniTaskの機能紹介

Slide 68

Slide 68 text

UniTaskTracker • await中のUniTaskを可視化するEditor拡張 • Unity Editorの [Window] -> [UniRx] -> [UniTask Tracker] • UniTaskがリークしていないか確認できる

Slide 69

Slide 69 text

UniTaskとキャンセル

Slide 70

Slide 70 text

⾮同期処理とキャンセル • ⾮同期処理はキャンセルを常に考慮しなくてはいけない • もろもろの理由で不要になった時に 実⾏中の処理を⽌めないといけない

Slide 71

Slide 71 text

UniTaskとキャンセル • UniTaskを使う上でも当然キャンセルの実装が必要 • リソースの解放、適切なタイミングでの停⽌、漏れのない中断、 これらは⼈間が考えて実装する必要がある

Slide 72

Slide 72 text

適切にキャンセルするために • CancellationToken • OperationCanceledException

Slide 73

Slide 73 text

適切にキャンセルするために • CancellationToken • OperationCanceledException

Slide 74

Slide 74 text

CancellationToken • ⾮同期処理にキャンセルを通知するためのC#標準機能 • 「キャンセル要求されたか?」のフラグを持つオブジェクト

Slide 75

Slide 75 text

CancellationToken

Slide 76

Slide 76 text

CancellationToken CanellationTokenSourceから⽣成

Slide 77

Slide 77 text

CancellationToken if⽂で判定 if( IsCancellationRequested ) throw new OperationCanceledException(); の省略記法

Slide 78

Slide 78 text

補⾜: GetCancellationTokenOnDestroy()

Slide 79

Slide 79 text

UniTaskとCancellationToken • UniTaskはCancellationTokenを使ってキャンセル判定を⾏う • キャンセル要求がくるとUniTaskはキャンセル状態になる • await中の場合はそこで処理が終了しawait以降は実⾏されない

Slide 80

Slide 80 text

CancellationTokenは省略しない • 「めんどくさいからキャンセル処理を書かない」はNG

Slide 81

Slide 81 text

Tokenが渡せない処理の場合のキャンセル • CancellationTokenの状態を⼿動で確認する • どのタイミングで処理を⽌めるのか、は⾃分で考える • キャンセル時はOperationCanceledExceptionを発⾏すればOK

Slide 82

Slide 82 text

⼿動でキャンセルチェックする例

Slide 83

Slide 83 text

適切にキャンセルするために • CancellationToken • OperationCanceledException

Slide 84

Slide 84 text

OperationCanceledException • UniTaskをキャンセル状態にするための特殊扱いな例外 • この例外が投げられるとUniTaskは「キャンセル状態」になる • この例外はエラーログに出ない

Slide 85

Slide 85 text

Throwするタイミング • キャンセル要求が外部からされた時にthrowする • 外から「処理を⽌めてね」って⾔われた時に投げる例外 • ⾃⼰判断で勝⼿に投げてよい例外ではない

Slide 86

Slide 86 text

キャンセル要求 • キャンセル要求はCancellationTokenを通じて送られる • ThrowIfCancellationRequested()が便利

Slide 87

Slide 87 text

キャンセルを上流まで伝播させる • この例外は上流まで“貫通”させる • try-catchを書くときはOperationCanceledExceptionだけ 素通りするようにしておく

Slide 88

Slide 88 text

キャンセルを上流まで伝播させる • この例外は上流まで“貫通”させる • try-catchを書くときはOperationCanceledExceptionだけ 素通りするようにしておく

Slide 89

Slide 89 text

悪⽤しない • キャンセル⽤途以外では利⽤しないこと • 本来の⽤途とは別の使い⽅をすると、そこから負債になる • 「処理に失敗したときにエラーログに出したくないから、 OperationCanceledException使っちゃおう」みたいなのはNG

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

UniTaskとキャンセル まとめ • CancellationTokenは可能な限り指定する • OperationCanceledExceptionは特殊なので扱いに注意

Slide 92

Slide 92 text

最後のまとめ

Slide 93

Slide 93 text

UniTask、結局何に使えばいいの? • 深く考えず使えそうなところでどんどん使おう • コルーチンの代替として使える • 他のコンポーネントが処理を終えるのを待つ、とかにも使える • むしろUniTaskが使えないことがストレスになるくらいには便利

Slide 94

Slide 94 text

他の⾮同期処理との使い分けは? • async/awaitを使うなら、UniTaskをまず使う • 理由が無い限り標準Taskは使わない • UniRx(Observable)は⽤途に応じて使う • Observableはイベント処理や、結果が複数になる⾮同期処理向け • Observableもawait可能だし、UniTaskとの相互変換もできる

Slide 95

Slide 95 text

まとめ • UniTaskめっちゃ便利だから使おう!!!! • あと今回の話の補⾜的なのをUniBook11に書きました • さらにUniRx+UniTask本、マダカイテルカラマッテネ…