Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
.NETの非同期戦略とUnityとの相互運用
Search
Yoshifumi Kawai
March 21, 2024
Technology
3
36k
.NETの非同期戦略とUnityとの相互運用
Game Developers Meeting Vol.61
GDM × Born Digital
Yoshifumi Kawai
March 21, 2024
Tweet
Share
More Decks by Yoshifumi Kawai
See All by Yoshifumi Kawai
CysharpのOSS群から見るModern C#の現在地
neuecc
2
20k
R3のコードから見る実践LINQ実装最適化・コンカレントプログラミング実例
neuecc
4
36k
他言語がメインの場合のRustの活用法 - csbindgenによるC# x Rust FFI実践事例
neuecc
7
40k
Modern High Performance C# 2023 Edition
neuecc
4
15k
CEDEC 2023 モダンハイパフォーマンスC# 2023 Edition
neuecc
8
99k
C#11 による世界最速バイナリシリアライザー「MemoryPack」の作り方
neuecc
1
41k
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
neuecc
1
400
Deep Dive async/await in Unity with UniTask(UniRx.Async)
neuecc
0
410
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
neuecc
2
1.2k
Other Decks in Technology
See All in Technology
ハッカー視点で学ぶサイバー攻撃と防御の基本
nomizone
2
1.4k
Whats_new_in_Podman_and_CRI-O_2025-06
orimanabu
3
160
Introduction to Sansan, inc / Sansan Global Development Center, Inc.
sansan33
PRO
0
2.6k
AIエージェントの継続的改善のためオブザーバビリティ
pharma_x_tech
3
290
Go Connectへの想い
chiroruxx
0
160
Introduction to Sansan Meishi Maker Development Engineer
sansan33
PRO
0
280
Monorepo Error Management: Automated Runbooks and Team-Targeted Alert Distribution
biwashi
1
140
(新URLに移行しました)FASTと向き合うことで見えた、大規模アジャイルの難しさと楽しさ
wooootack
0
570
Tensix Core アーキテクチャ解説
tenstorrent_japan
0
340
Oracle Cloud Infrastructureデータベース・クラウド:各バージョンのサポート期間
oracle4engineer
PRO
48
33k
Ретроспективный взгляд на Vue 3. Даша Сабурова, Vue-разработчик Lamoda Tech
lamodatech
0
950
Kafka vs. Pulsar: Performance Evaluation by Petabyte-Scale Streaming Platform Providers
lycorptech_jp
PRO
1
350
Featured
See All Featured
StorybookのUI Testing Handbookを読んだ
zakiyama
30
5.8k
The Straight Up "How To Draw Better" Workshop
denniskardys
233
140k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
35
2.3k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
32
5.9k
Raft: Consensus for Rubyists
vanstee
138
7k
Building Applications with DynamoDB
mza
95
6.4k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
30
2.1k
We Have a Design System, Now What?
morganepeng
52
7.6k
Art, The Web, and Tiny UX
lynnandtonic
299
21k
Unsuck your backbone
ammeep
671
58k
4 Signs Your Business is Dying
shpigford
183
22k
Code Review Best Practice
trishagee
68
18k
Transcript
.NETの非同期戦略とUnityとの相互運用 Game Developers Meeting Vol.61 GDM x Born Digital 2024-03-21
Yoshifumi Kawai / Cysharp, Inc.
About Speaker 河合 宜文 / Kawai Yoshifumi / @neuecc Cysharp,
Inc. - CEO/CTO 株式会社Cygamesの子会社として2018年9月設立 C#関連の研究開発/OSS/コンサルティングを行う Microsoft MVP for Developer Technologies(C#) since 2011 CEDEC AWARDS 2022エンジニアリング部門優秀賞 .NETのクラスライブラリ設計 改訂新版 監訳 50以上のOSSライブラリ開発(UniRx, UniTask, MessagePack C#, etc...) C#では世界でもトップレベルのGitHub Star(合計30000+)を獲得
最近のOSS R3 - The new future of dotnet/reactive and UniRx
https://github.com/Cysharp/R3/ Unity使いの人に一言でいえば、UniRx2 UniTaskなどasync/awaitとの共存を意識して現代的に再設計 ObsevableTrackerというリーク確認ウィンドウ付きで便利 Claudia - Unofficial Anthropic Claude API client for .NET. https://github.com/Cysharp/Claudia UnityでもRuntime/Editorともに動きます! EditorでAIを活用したワークフロー改善 Runtimeで直接的なAIゲームを作ったりできます! 超レコメンデッド
Two Decades of Asynchronous Programming in C#
.NET Unity async/await Coroutine Rx UniRx UniTask Awaitable Channel ValueTask
EAP TAP APM APM = Asynchronous Programming Model(Begin/End) EAP = Event-based Asynchronous Pattern TAP = Task-based Asynchronous Pattern Rx = Reactive Extensions IAsyncEnumerable TPL Dataflow R3 C#誕生初期(2000年)から現在(2024年) までの24年間の非同期処理 TPL(Parallel) ThreadPool C# Job System
.NET Unity async/await Coroutine Rx UniRx UniTask Awaitable Channel ValueTask
EAP TAP APM IAsyncEnumerable TPL Dataflow R3 初期に構築されたものはさようなら TPL(Parallel) ThreadPool C# Job System
.NET Unity async/await Coroutine Rx UniRx UniTask Awaitable Channel ValueTask
EAP TAP APM IAsyncEnumerable TPL Dataflow R3 TPL(Parallel) ThreadPool C# Job System IValueTaskSource(.NET Standard 2.1), PoolingAsyncValueTaskMethodBuilder(.NET 6)などの強化 IThreadPoolWorkItem(.NET Core 3.0)の追加 フルManaged(C#)実装化(.NET 6)などの強化 Parallel.ForEachAsync(.NET 6) Parallel.ForAsync(.NET 8)の追加 生存してるものは折々強化さ れています!
.NET Unity async/await Coroutine Rx UniRx UniTask Awaitable Channel ValueTask
EAP TAP APM IAsyncEnumerable TPL Dataflow R3 UniTask, Awaitableは共存(むしろどち らかというとUniTaskメインに使う) 詳しくはUnityプログラミング・バイ ブル R6号に執筆したので読もう! TPL(Parallel) ThreadPool
.NET Unity async/await Coroutine Rx UniRx UniTask Awaitable Channel ValueTask
EAP TAP APM IAsyncEnumerable TPL Dataflow R3 TPL(Parallel) ThreadPool go to R3; C# Job System
.NET Unity async/await Coroutine Rx UniRx UniTask Awaitable Channel ValueTask
EAP TAP APM IAsyncEnumerable TPL Dataflow R3 TPL(Parallel) ThreadPool C# Job System Unityで使いたい場合、NuGet経由で入れる or UniTaskに簡易化したChannelがあります
NuGet in Unity
NuGetをUnityで使う 標準ライブラリが使えるシチュエーションの増加 Unityの.NET Standard 2.1準拠によって使えるライブラリが増えた Source Generatorも動作(Unity 2022.3.12f1以降ならRoslyn 4.3.0まで行ける) IL2CPP時のリフレクション・WebGL利用時のThreadPool利用など
を回避できれば、多くのライブラリが動作する 依存の解決 Managed DLL同士の依存関係を素直に解決するならNuGetに頼っ たほうがいいし、.NET x Unityで共通化していく際に実体が別物 (MessagePack-CSharpなど)だとかなり不便 .NETが近年NativeAOTの強化につき、リ フレクション利用に気を使いだしてきて、 結果としてIL2CPPでも問題なく動作する ようになってきた 最近のCysharpライブラリは NuGet化を推し進めています
NuGetをUnityで使う NuGetForUnity https://github.com/GlitchEnzo/NuGetForUnity シンプルにNuGetサーバーからDLLを落としてくるエディタ拡張 挙動が分かりやすくて良い。ただし、コードがコンパイルできな い時にはエディタ拡張も動かない→DLLが落とせない→一生コンパ イルできない、などの拡張ゆえのはまりどころもある UnityNuGet https://github.com/xoofx/UnityNuGet UnityのPackage
Managerの追加レジストリとして動作する UPMとして自然な挙動、ただしDLL配布サーバーは独自サーバー Cysharp的にはこちらを薦めています OpenUPMと合わせたりでこちらのほ うが扱いやすい、という意見もあり
Task/ValueTaskとThreadPool .NETとUnityの共通非同期インターフェイス UniTaskやAwaitableはUnity専用 .NETと共通で非同期処理を実装するならTask/ValueTaskで表す ThreadPool行きを避ける Unityの提供する非同期メソッドは完了時にエンジン側でメインス レッドに戻してくれる(AsyncOperationなど) 不要なオーバーヘッドが発生しているかもしれない うっかりThreadPool内に入るとUnityのAPIが呼べない! そしてWebGLで動作しない
UniTaskを使えば何も考えなくてもいい(最高!)けれど、 Task/ValueTaskを使わなければいけないなら……?
Continue on ThreadPool 1 async void Start() { // SynchronizationContext.SetSynchronizationContext(null);
var tcs = new TaskCompletionSource<object>(); StartCoroutine(NextFrame(tcs)); await tcs.Task; Debug.Log(Thread.CurrentThread.IsThreadPoolThread); } IEnumerator NextFrame(TaskCompletionSource<object> tcs) { yield return null; ThreadPool.QueueUserWorkItem(_ => tcs.TrySetResult(null)); } False ThreadPool上で継続を流す場合 UnityはデフォルトでUnitySynchronizationContextがセット されていて、await時にUnityのメインスレッドに戻す
Continue on ThreadPool 2 async void Start() { SynchronizationContext.SetSynchronizationContext(null); var
tcs = new TaskCompletionSource<object>(); StartCoroutine(NextFrame(tcs)); await tcs.Task; Debug.Log(Thread.CurrentThread.IsThreadPoolThread); } IEnumerator NextFrame(TaskCompletionSource<object> tcs) { yield return null; ThreadPool.QueueUserWorkItem(_ => tcs.TrySetResult(null)); } True メインスレッドに戻す機構がな いのでThreadPool上のまま UnitySynchronizationContextがセットされていない場合
ConfigureAwait : (unity-context | true) async void Start() { //
SynchronizationContext.SetSynchronizationContext(null); var tcs = new TaskCompletionSource<object>(); StartCoroutine(NextFrame(tcs)); await tcs.Task; // .ConfigureAwait(false); Debug.Log(Thread.CurrentThread.IsThreadPoolThread); } IEnumerator NextFrame(TaskCompletionSource<object> tcs) { yield return null; tcs.TrySetResult(null); } False このケースでは実は UnitySynchronizationContextは「使われ ていないの」でSynchronizationContextの パフォーマンス上のペナルティはない Unityのメインスレッド内でTaskの継続を呼ぶ
ConfigureAwait : (null-sync | true) async void Start() { SynchronizationContext.SetSynchronizationContext(null);
var tcs = new TaskCompletionSource<object>(); StartCoroutine(NextFrame(tcs)); await tcs.Task; // .ConfigureAwait(false); Debug.Log(Thread.CurrentThread.IsThreadPoolThread); } IEnumerator NextFrame(TaskCompletionSource<object> tcs) { yield return null; tcs.TrySetResult(null); } False nullをセットしてもThreadPoolにはいかない、というのも コルーチンがPlayerLoop上でTaskを継続させているので
ConfigureAwait : (unity-context | false) async void Start() { //
SynchronizationContext.SetSynchronizationContext(null); var tcs = new TaskCompletionSource<object>(); StartCoroutine(NextFrame(tcs)); await tcs.Task.ConfigureAwait(false); Debug.Log(Thread.CurrentThread.IsThreadPoolThread); } IEnumerator NextFrame(TaskCompletionSource<object> tcs) { yield return null; tcs.TrySetResult(null); } True メインスレッド上でTaskを継続させていても ConfigureAwait(false)によってThreadPool行き
ConfigureAwait : (null-sync | false) async void Start() { SynchronizationContext.SetSynchronizationContext(null);
var tcs = new TaskCompletionSource<object>(); StartCoroutine(NextFrame(tcs)); await tcs.Task.ConfigureAwait(false); Debug.Log(Thread.CurrentThread.IsThreadPoolThread); } IEnumerator NextFrame(TaskCompletionSource<object> tcs) { yield return null; tcs.TrySetResult(null); } True Synchronization Contextの有無は関係ない ConfigureAwait(false)によってThreadPoolへ行く
Task/ValueTaskのThreadPool行き条件 Task.Run ある意味、明示的なThreadPool行き JobSystemほど制限がないため、CPUを使った並列処理をカジュアル に行いたいときには便利 TaskCompletionOptions.RunContinuationAsynchronously TaskCompletionSourceのTrySetResult時に、trueで必ずThreadPool行 きするオプション。ネットワーク系ライブラリでよくある。Channel ではOptions {
AllowSynchronousContinuations } の意味が、これをど ちらに設定するかということをさす。falseにすると (RunContinuationAsynchronouslyと逆)場合によりThreadPool行きする
Task/ValueTaskのThreadPool行き条件 ConfigureAwait(false) 問答無用でThreadPoolに飛ばすの意(ThreadPool上での継続の場合 はほとんどの場合、そのままそのスレッドのまま処理する) .NETのasync/awaitのプラクティスとして「ライブラリのasyncメソッ ドはConfigureAwait(false)を使うこと」というのがあり、場合によっ て(あるいはほとんどの場合)Unity側から避ける術がない なお、ConfigureAwaitを使わない場合でも、SynchronizationContext は常に使われるわけではなく、SynchronizationContextが同一の場合、 Unityの場合でいうとメインスレッド上で継続される場合はインライ
ンで処理されていくので、UnitySynchronizationContextのオーバー ヘッドは発生しないようになっている。
Task/ValueTaskのThreadPool行き条件 ConfigureAwait(false) 問答無用でThreadPoolに飛ばすの意(ThreadPool上での継続の場合 はほとんどの場合、そのままそのスレッドのまま処理する) .NETのasync/awaitのプラクティスとして「ライブラリのasyncメソッ ドはConfigureAwait(false)を使うこと」というのがあり、場合によっ て(あるいはほとんどの場合)Unity側から避ける術がない なお、ConfigureAwaitを使わない場合でも、SynchronizationContext は常に使われるわけではなく、SynchronizationContextが同一の場合、 Unityの場合でいうとメインスレッド上で継続される場合はインライ
ンで処理されていくので、UnitySynchronizationContextのオーバー ヘッドは発生しないようになっている。 なお、UniTaskはこういった面倒ごとが絶対に発生しないよ うにConfigureAwaitというシステムそのものを排除している 非同期処理を含むNuGetライブラリをUnity WebGL対応も考 慮して作るのはめちゃくちゃ大変……、という教訓をR3の実 装で得ました(R3はきっちり全回避してWebGL対応です!) この「ライブラリはConfigureAwait(false)必須」プラクティス、現代 では不要説もある。昔、ASP.NETにSyncContextがあった&非同期対 応が不十分のためしょうがなく .Resultしたらデッドロックしまくっ た、のを回避するため生まれたプラクティスなので(現在は ASP.NETはSyncContextはない(ただしBlazorにはある)&フレームワー クが完全に非同期を考慮しているので.Resultすることもないため)
None