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
10
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
6
NAND から全加算器まで (CPU の仕組み超入門)
ikorin24
0
54
Other Decks in Programming
See All in Programming
レガシープロジェクトで最大限AIの恩恵を受けられるようClaude Codeを利用する
tk1351
2
550
未来を拓くAI技術〜エージェント開発とAI駆動開発〜
leveragestech
2
170
バイブコーディングの正体——AIエージェントはソフトウェア開発を変えるか?
stakaya
5
1k
ワープロって実は計算機で
pepepper
2
1.4k
新世界の理解
koriym
0
140
技術的負債で信頼性が限界だったWordPress運用をShifterで完全復活させた話
rvirus0817
1
2k
エンジニアのための”最低限いい感じ”デザイン入門
shunshobon
0
120
Introduction to Git & GitHub
latte72
0
120
兎に角、コードレビュー
mitohato14
0
150
令和最新版手のひらコンピュータ
koba789
14
7.9k
コーディングは技術者(エンジニア)の嗜みでして / Learning the System Development Mindset from Rock Lady
mackey0225
2
560
あのころの iPod を どうにか再生させたい
orumin
2
2.5k
Featured
See All Featured
Faster Mobile Websites
deanohume
309
31k
Side Projects
sachag
455
43k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
50k
A better future with KSS
kneath
239
17k
The Cost Of JavaScript in 2023
addyosmani
53
8.8k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
358
30k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Raft: Consensus for Rubyists
vanstee
140
7.1k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
139
34k
Git: the NoSQL Database
bkeepers
PRO
431
65k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
53
2.9k
Music & Morning Musume
bryan
46
6.7k
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)