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
C# async/await 勉強会
Search
Hiroki Kamiyoshikawa
May 05, 2025
Programming
0
15
C# async/await 勉強会
C# の非同期処理における async/await の構造や動作原理についての解説。
(2023/1/27 社内勉強会用)
Hiroki Kamiyoshikawa
May 05, 2025
Tweet
Share
More Decks by Hiroki Kamiyoshikawa
See All by Hiroki Kamiyoshikawa
コンパイラを作ろう
ikorin24
0
11
NAND から全加算器まで (CPU の仕組み超入門)
ikorin24
0
170
Other Decks in Programming
See All in Programming
Fluid Templating in TYPO3 14
s2b
0
130
KIKI_MBSD Cybersecurity Challenges 2025
ikema
0
1.3k
HTTPプロトコル正しく理解していますか? 〜かわいい猫と共に学ぼう。ฅ^•ω•^ฅ ニャ〜
hekuchan
2
690
CSC307 Lecture 01
javiergs
PRO
0
690
「ブロックテーマでは再現できない」は本当か?
inc2734
0
1k
CSC307 Lecture 04
javiergs
PRO
0
660
AI時代のキャリアプラン「技術の引力」からの脱出と「問い」へのいざない / tech-gravity
minodriven
21
7.4k
360° Signals in Angular: Signal Forms with SignalStore & Resources @ngLondon 01/2026
manfredsteyer
PRO
0
130
AIエージェントのキホンから学ぶ「エージェンティックコーディング」実践入門
masahiro_nishimi
6
600
コントリビューターによるDenoのすゝめ / Deno Recommendations by a Contributor
petamoriken
0
210
余白を設計しフロントエンド開発を 加速させる
tsukuha
7
2.1k
React 19でつくる「気持ちいいUI」- 楽観的UIのすすめ
himorishige
11
7.5k
Featured
See All Featured
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.3k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
100
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
130
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
93
How STYLIGHT went responsive
nonsquared
100
6k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.3k
Utilizing Notion as your number one productivity tool
mfonobong
3
220
Code Review Best Practice
trishagee
74
20k
Optimizing for Happiness
mojombo
379
71k
How to make the Groovebox
asonas
2
1.9k
Transcript
C# async/await 勉強会 2021/11/08 作成者 ikorin
async/await の登場 var foo = new Foo(); foo.Completed += result
=> { Debug.Log($" 答えは {result} です"); }; foo.Do(); public class Foo { public event Action<int> Completed; public void Do() { new Thread(() => { // とても重い計算処理 var result = 1 + 1; Completed?.Invoke(result); }).Start(); } } var foo = new Foo(); var result = await foo.DoAsync(); Debug.Log($" 答えは {result} です"); public class Foo { public async UniTask<int> DoAsync() { await UniTask.SwitchToThreadPool(); // とても重い計算処理 return 1 + 1; } } 完了イベントによるコールバック async/await による実装 多段に非同期処理を書くと可読性が悪い 非同期処理内の例外のハンドリングが困難 await を並べるだけで多段に非同期実行可能 非同期処理内の例外も呼び出し元まで伝播する
非同期処理とバックグラウンド処理 バックグラウンドスレッド実行のために非同期処理を簡潔に書くための構文が必要 GUI アプリケーションでは重い処理をバックグラウンドスレッドで実行したい Task, async/await 構文 async/await 登場から UniTask
登場まで UniTask Unity バックグラウンド実行することを主目的として async/await と Task を導入したため、メインスレッド内のみで非同期処理をする API は Task には用意されていない ・スレッドの概念と非同期処理の概念を分離している ・Unity で使える各種便利機能 Unity 以外ではバックグラウンド処理以外で非同期処理を扱う必要がほぼないため、 決して Task と async/await の作成者が 非同期とスレッドの概念を混同しているわけではない。
Time Frame n n+1 n+2 n+a EarlyUpdate Update LateUpdate object1
object2 object3 object1 object2 object3 object1 object2 object3 CPU の処理の流れ プログラマが実装したい処理の流れ CPU の処理の流れとプログラマが実装したい処理の流れの乖離 Unity Player Loop 両者の流れが違うため、実装が複雑になる
Time Frame n n+1 n+2 n+a EarlyUpdate Update LateUpdate object1
object2 object3 object1 object2 object3 object1 object2 object3 'await' = その位置で CPU の処理の流れをジャンプする ( かもしれない) 'async' = そのメソッドの中でジャンプする ( かもしれない) ことを示すマーク await UniTask.DelayFrame(a) await UniTask.Yield(PlayerLoopTiming.EarlyUpdate) await UniTask.CompletedTask ( ジャンプしない) async と await の意味 await によってプログラマにとって都合のいい処理の流れを作れる Unity Player Loop
Time Frame n n+1 n+2 n+a EarlyUpdate Update LateUpdate object1
object2 object3 await はスレッド間をジャンプすることもできる Background Thread Main Thread await UniTask.SwitchToThreadPool() await UniTask.SwitchToMainThread(PlayerLoopTiming.LateUpdate) object1 object2 object3 await によるスレッド間移動 Unity Player Loop
同期コンテキスト (SynchronizationContext) await UniTask.SwitchToThreadPool() await UniTask.Yield() await UniTask.CompletedTask ( ジャンプしない)
await Task.Run(action) ( メインスレッドで開始) action Main Thread Thread A await Task.Run(action) ( 非メインスレッドで開始) action Thread A Thread B await Task.Run(action).ConfigureAwait(false) ( メインスレッドで開始) action Main Thread Thread A Main Thread Thread A await UniTask.DelayFrame(a) メインスレッドの同期コンテキストの作用で 暗黙的にメインスレッドに戻る action Thread A Thread B await Task.Run(action).ConfigureAwait(false) ( 非メインスレッドで開始) 明示的に同期コンテキストをつかまないよう指示 (.ConfigureAwait(false)) 非メインスレッド (Thread A) には 同期コンテキストは存在しないため Thread B で継続 同期コンテキストは存在しないため ConfigureAwait の指定は true でも false でも 戻らない UniTask Task UniTask は同期コンテキストをキャプチャしない。 終了時のスレッドでそのまま継続処理を実行。 Task は同期コンテキストがあればキャプチャする。 キャプチャすると元のスレッドに戻り継続処理を実行。
async メソッドの戻り値 await Hoge(true) DoSomething() Main Thread Thread A async
UniTask Hoge(bool xxx) { if (xxx) { return; } await UniTask.SwitchToThreadPool(); DoSomething(); return; } Main Thread await Hoge(false) await Hoge(true) DoSomething() Main Thread Thread A async Task Hoge(bool xxx) { if (xxx) { return; } await UniTask.SwitchToThreadPool(); DoSomething(); return; } Main Thread await Hoge(false) async Task はメインスレッドで await すると 暗黙的にメインスレッドに戻る ( 同期コンテキストをキャプチャする) async UniTask は終了時のスレッドで継続する ( 同期コンテキストをキャプチャしない) async メソッドはコンパイラによって async を使わない形に変形される。async は糖衣構文。 その戻り値の UniTask, Task はコンパイラが自動生成する。 UniTask Task
await によるジャンプはデリゲートのキュー UniTask Task Main Thread Thread A async UniTask
Piyo() { await UniTask.SwitchToThreadPool(); DoSomething(); return; } await Piyo() Piyo() DoSomething() UniTask.SwitchToThreadPool() Thread A のキューに DoSomething を入れる キューから DoSomething が とりだされて実行 Piyo() の戻り値は UniTask であるため メインスレッドの同期コンテキストを キャプチャしていない。 そのまま継続処理を実行 Main Thread Thread A Piyo() DoSomething() UniTask.SwitchToThreadPool() UniTask と同じ Piyo() の戻り値は Task であるため メインスレッドの同期コンテキストを キャプチャしている。 同期コンテキストのキューに 継続処理をポスト UniTask と同じ 同期コンテキストのキューから 継続処理を取り出して実行 await Piyo() async Task Piyo() { await UniTask.SwitchToThreadPool(); DoSomething(); return; }
async void はなぜ非推奨か Main Thread Thread A Main Thread Thread
A 例外発生 catch Main Thread Thread A await による待機呼び出し await による呼び出しは どこで例外が発生しても await を追跡して遡る Main Thread Thread A Main Thread Thread A 例外発生 catch async void による待機なし呼び出し async void は await できないため ジャンプした処理を追跡できない Main Thread Thread A 未キャッチ例外が漏れる async void は開始した処理の 終了を検知できないため 継続処理を書けない 同期的な例外はキャッチ可能 async void を await できないのは何が困るのか async void を書いてもいい条件 ・未キャッチ例外が確実に漏れないこと ・開始した非同期処理の終了を検知する必要がないこと (fire and forget)