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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Yoshifumi Kawai
March 21, 2024
Technology
3
50k
.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
35k
R3のコードから見る実践LINQ実装最適化・コンカレントプログラミング実例
neuecc
5
50k
他言語がメインの場合のRustの活用法 - csbindgenによるC# x Rust FFI実践事例
neuecc
7
41k
Modern High Performance C# 2023 Edition
neuecc
4
16k
CEDEC 2023 モダンハイパフォーマンスC# 2023 Edition
neuecc
9
120k
C#11 による世界最速バイナリシリアライザー「MemoryPack」の作り方
neuecc
1
42k
Unityによるリアルタイム通信とMagicOnionによるC#大統一理論の実現
neuecc
1
470
Deep Dive async/await in Unity with UniTask(UniRx.Async)
neuecc
0
500
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
neuecc
2
1.9k
Other Decks in Technology
See All in Technology
私たち準委任PdEは2つのプロダクトに挑戦する ~ソフトウェア、開発支援という”二重”のプロダクトエンジニアリングの実践~ / 20260212 Naoki Takahashi
shift_evolve
PRO
2
230
Agile Leadership Summit Keynote 2026
m_seki
1
680
OCI Database Management サービス詳細
oracle4engineer
PRO
1
7.4k
Amazon Bedrock Knowledge Basesチャンキング解説!
aoinoguchi
0
170
マネージャー視点で考えるプロダクトエンジニアの評価 / Evaluating Product Engineers from a Manager's Perspective
hiro_torii
0
190
AIが実装する時代、人間は仕様と検証を設計する
gotalab555
1
640
ファインディの横断SREがTakumi byGMOと取り組む、セキュリティと開発スピードの両立
rvirus0817
1
1.7k
CDKで始めるTypeScript開発のススメ
tsukuboshi
1
580
Red Hat OpenStack Services on OpenShift
tamemiya
0
140
Cosmos World Foundation Model Platform for Physical AI
takmin
0
980
旅先で iPad + Neovim で iOS 開発・執筆した話
zozotech
PRO
0
100
Claude_CodeでSEOを最適化する_AI_Ops_Community_Vol.2__マーケティングx_AIはここまで進化した.pdf
riku_423
2
610
Featured
See All Featured
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
280
Building a Scalable Design System with Sketch
lauravandoore
463
34k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
0
260
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Mobile First: as difficult as doing things right
swwweet
225
10k
Building Adaptive Systems
keathley
44
2.9k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
0
150
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
340
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
450
ラッコキーワード サービス紹介資料
rakko
1
2.3M
Discover your Explorer Soul
emna__ayadi
2
1.1k
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
350
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