2018年9月15日「Unity 非同期完全に理解した勉強会」というイベントに参加してきたので、その備忘録です。
目次
概要
某プテピピックで出てきた「完全に理解した(わかってない)」というフレーズを冠したイベントの3回目です。今回の主催はとりすーぷさんですが、運営は毎度おなじみもんりぃ先生、青木ととさんが主に担当しています。
なんと休憩込みで5時間に及ぶセッションが用意されてたり、MVPを取った豪華登壇者がズラッと並んだり、本当にユーザーイベントか疑わしいレベルでした。(まあUTJの中の人もめっちゃ噛んでますが)
会場は銀座にある「NIFcLounge(ニフクラウンジ)」です。富士通のグループ会社、富士通クラウドテクノロジーズ株式会社さんのオフィスです。IT関連の勉強会には無料で貸出をしているようなので、主催者の方は利用を検討してみてはいかがでしょうか。ちなみにこの会場、今回は定員170名でしたが、パーティションで区切って10人程度?からでも利用できるようです。
またニフクラはクラウドサービスで、Unity向けのサービスもあるのでお世話になってる方もいるのではないでしょうか。かくいう自分も利用しています。(少し古いですが過去記事)
ライブ配信
ニコ生タイムシフト
2018年9月22日まで閲覧可能。後日、録画したものをYoutubeにアップするそうです。
「Deep Dive async/await in Unity」
河合 宜文さん Twitter: @neuecc
資料
Rxについて
Rxは時間軸で解釈してなんでも統一的に扱えるのが強みであり弱み
- For,try-catchは見た目通りかけない→書かないほうがいい
- 非同期はasync/awaitの方が適役
- Rxはイベントストリームを活用する
async/awaitについて
async/awaitはマルチスレッドではない!!!!
なっちゃうことも稀によくある、という話
Taskが持つ悲しみの歴史
- Taskにasync/awaitを乗っけて実装したため、イケてない部分が多く残った
- UnityはC#のアプデが遅れたためそこをスキップできる
- そのためのUniTask(C#ではValueTask)
async/awaitの動作
Awaitは自動的にそこでぶった切ってコールバックになるイメージ
非同期処理を書いても非同期じゃないときもある
例:キャッシュ機構→初回は必ず非同期だが、次からはキャッシュを同期処理で呼ぶ
おかげでパフォーマンスがよい
asyncメソッドのなかにawaitがいくつあっても内部で冗長にならない→まとめた方が効率がよい
UniTask
UniTaskはC#7(unity2018.3)の機能を使った、Unity専用フレームワークで、以下のような点を理由に作られた
- Unityは基本シングルスレッドなのでTaskは実はいらない
- async/awaitはTaskのおかげで(意図せず)マルチスレッド動作をしてしまう
- マルチスレッドを考慮しなければ効率が上がるのでは?
?WebGLはマルチスレッドが使えないので、unityroomで引っかかるのはこのへんのせいかも
機能色々
UniTaskはコルーチンを代替出来る(高速軽量な!)処理がある
実はJobSystemもawait出来る
async/awaitのキャンセル
キャンセルが面倒
→ひたすらcancellationtokenが伝搬する、めんどい(が、そうするしかない
キャンセルがある前提だとやばい
OnDestroyで処理するのが便利
AsyncEventhandling
Eventをasync/awaitで実装できる
→でもRx(ReactivePropaty)のほうが基本よい
まとめ
性能も手法も問題ない、すぐにでも使い始めたほうがよい
おまけ
「async/await のしくみ」
岩永信之さん Twitter: @ufcpp
資料
最新版のUnityでC#7.2が使えるよ(C#の最新は7.3だよ!…あれ?)
非同期とは
- 同時実行
- CPUの処理を奪い合う
- UIスレッド=Unityではメインスレッドとよばれる
- ユーザの入力の受付をする。描画もされる
- ここを重用するとフリーズしたりする。UXがさがる
- 並列実行
- 複数CPUをつかう
- IO待ち
- CPUとそれ以外のやりとりの待ち時間(数桁レベルで速度が違う)
- awaitの使い所
- ユーザー入力受け取り後の処理 メインスレッドを止めない
- CPUとそれ以外のやりとりの待ち時間(数桁レベルで速度が違う)
C#標準の非同期処理
- Thread
- OSの強権をつかうので重い、使うことはそうない
- ThreadPool
- 事前にスレッドを立てて分配
- こちらも低レイヤーなので使うことは少ない
- Task
- 非同期処理の続きを書けるクラス=コールバック
- コールバックがめんどう
- await
- Task(のコールバック)を便利にした
- UnityGCの性能は低い
async/await
同期っぽいコードから非同期コードを生成してくれる
その他
ValueTaskはいつから使える?→割と古いバージョンから使える
「実践! ライブコーディングで覚えるasync/await」
細田幸治さん 株式会社オレンジキューブ所属 FaceBook : kouji.hosoda
資料(内容は動画アーカイブを参照)
For文の中でもawaitで待てる
→ユーザーの入力待ち(ページ送りとか)に便利
タプル→C#7「名前のない型」を作る機能
メンバーが増えたら(4つとか)独自クラスを作ろう
splitは結構重いので(非同期で)先に処理したほうがよい
Try-chatch
→必ず行われるべき処理はfinallyに書く
ラムダ式はエラーの場所がわかりにくいので中身を増やしすぎないこと
先にロードしてあとからawaitする
→awaitのタイミングですでにロードが完了してる場合、非同期しない処理として最適化される。えらい。
同期(直列)で書きたいけど非同期にせざるを得ない場所でawaitが便利
「はたらくスレッド」
名雪 通さん & 安原 祐二さん Twitter: @tnayuki ユニティ・テクノロジーズ・ジャパン所属
資料
このセッション、是非動画でみて欲しい
安原さんと名雪さんの小芝居がとても面白かったです(笑)
「Observableの非同期処理への活用」
とりすーぷさん Twitter: @toRisouP
資料
非同期処理とは?
結果を待たないで次に行く
マルチスレッドとは限らない
結果の扱い、エラーハンドリング、キャンセル、etc考慮することが多く難しい
Unityで使えるやつ
- Unityコルーチン
- C# async/await
- UniRx(observable)
- UniRx(Async)
- 独自実装
使いやすさ
async/await > UniRx
本題
UniRxは「イベント処理ライブラリ」だけではなく「非同期処理」にも使える
Observable
Observable=ストリーム
イベント処理に強い、ゲームはイベントが多い→ゲーム作りで便利
イベント処理と非同期処理
イベント処理→メッセージ数が不定 OnTriggerとかそのへん
非同期処理→メッセージ数が必ず1つ
Observableで同じように処理できる
コルーチンと比較
Observableで包むと使いやすくなる、後からでもOK
実行結果→IObservable<T>で扱える
直列処理→.ContinueWithで繋げれば非同期で待てる
並行処理→Observable.WhenAllで包めばOK
UniRxの多様なエラーハンドリングOperator
- Catch 差し替える(任意)
- CatchIgnore 差し替える(Empty)
- Retry やり直す、使い所に注意
- OnErrorRetry Catch+Retry
- Timeout
AsyncSubject
非同期処理をするためのSubject
- メッセージを1つだけキャッシュ
- あとからsubscribeしても結果をとれる
- async/awaitで待てる
用途
- 非同期処理の通知
- コンポーネント初期化順序
- 初期化=非同期処理が連鎖する、と捉えて書き換える
PublishLast()
Hot変換するOperator
- Cold→subscribeする前の(動いてない)Observable
- Hot→subscribeした(動いてる)Observable
- LinQと似たような性質
Hot変換=1度Subscribe()する=LinQを1回実行してlistに詰め直す
仲間
- Publish()
- Publis(T initValue)
- PublisLast()
- Replay()
メリット→Observableを先に実行できる&結果をキャッシュできる
デメリット→失敗もキャッシュする=エラーハンドリングが不可能になる
Observable.Start()
Task.Runとほぼ同じ
同期処理を簡単にマルチスレッドの非同期処理にできる
Observable.Create()
自前のObservableをつくれる
使い方は無限大
既存の非同期処理をラップできたりする、便利
→ニフクラSDKをObservable化、など
そのまま使うとメインスレッドをブロッキングする
→SubscribeOn(Scheduler.ThreadPool)でThreadPoolに移動できる
Observable.FromCoroutine
コルーチンをObservableに変換できる
コルーチンの最大の欠点(結果を取り出せない)が解消できる
ToYieldInstruction
ObservableをIEnumeratorに変換する
コルーチンでasync/awaitぽい処理ができる
Observable.FromCoroutineとToYieldInstructionでasync/awaitが使えない環境でも戦える
まとめ
まずはasync/awaitを使う→処理が難しい場合はObservableで書く
Hot/Coldに要注意!!!
→DoOnSubscribed、Providerやoperatorでチェック
「いまさらだけど確認しておこう Coroutine」
むろほしりょうたさん Twitter: @RyotaMurohoshi
資料
ブログ
#Unity非同期完全に理解した 勉強会でCoroutineについてLTしました
古い環境を使わざるを得ないこともある…
OnTriggerとかもコルーチンにできるよ
「UnmanagedThreadノススメ」
うえしたさん Twitter: @ueshita
資料
「勧めるとは言っていない」
「ソシャゲ開発で非同期処理の活用法」
ろっさむさん Twitter: @4_mio_11
資料 未公開?
Unity4.xで続けざるを得なかった開発の悲しいお話でした