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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
CSC307 Lecture 06
javiergs
PRO
0
690
CSC307 Lecture 07
javiergs
PRO
1
560
CSC307 Lecture 08
javiergs
PRO
0
670
AWS re:Invent 2025参加 直前 Seattle-Tacoma Airport(SEA)におけるハードウェア紛失インシデントLT
tetutetu214
2
120
ぼくの開発環境2026
yuzneri
0
240
SourceGeneratorのススメ
htkym
0
200
生成AIを使ったコードレビューで定性的に品質カバー
chiilog
1
280
今こそ知るべき耐量子計算機暗号(PQC)入門 / PQC: What You Need to Know Now
mackey0225
3
380
プロダクトオーナーから見たSOC2 _SOC2ゆるミートアップ#2
kekekenta
0
220
コマンドとリード間の連携に対する脅威分析フレームワーク
pandayumi
1
460
Oxlint JS plugins
kazupon
1
1k
React Native × React Router v7 API通信の共通化で考えるべきこと
suguruooki
0
100
Featured
See All Featured
Believing is Seeing
oripsolob
1
58
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
230
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
100
Optimising Largest Contentful Paint
csswizardry
37
3.6k
Ethics towards AI in product and experience design
skipperchong
2
200
First, design no harm
axbom
PRO
2
1.1k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
170
Code Reviewing Like a Champion
maltzj
527
40k
Paper Plane
katiecoart
PRO
0
46k
Docker and Python
trallard
47
3.7k
A better future with KSS
kneath
240
18k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
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)