Slide 1

Slide 1 text

1. 非同期処理ってむずかしい (><)¶

Slide 2

Slide 2 text

1.1. はじめに¶ • C# 5.0から採用された • async / await • 手軽に非同期処理が書ける

Slide 3

Slide 3 text

1.2. わたし¶ おなまえ たむらかずひこ twitter ktz_alias • ひよっこC#使い (Lv.1) • gitムズ過ぎ • bzrかわいいよbzr

Slide 4

Slide 4 text

1.3. async の決まり事¶ • 戻り値がTaskまたはvoid • コード例は後で

Slide 5

Slide 5 text

1.4. async Task¶ • 完了するまで後続の処理を止めてくれる • その間は一旦、呼び出し元へ戻る • 呼び出し元で結果欲しい場合に使う

Slide 6

Slide 6 text

1.5. async Task¶ public async void Foo() { var bar = await BarAsync(); // 上の結果待ってから続行 return await BazzAsync(bar); } public async Task BarAsync() { ... }

Slide 7

Slide 7 text

1.6. async void¶ • 結果を待たず、呼び出し元に戻る • 投げっ放しジャーマン

Slide 8

Slide 8 text

1.7. async void¶ public async void Foo() { await BarAsync(); // 上の結果待たずに続行 await BazzAsync(); } public async void BarAsync() { ... }

Slide 9

Slide 9 text

1.8. await の決まり事¶ • asyncなメソッドでのみ使用可 • finally, catch, lock内では使用不可 • awaitableなメソッドにのみ付けれる

Slide 10

Slide 10 text

1.9. awaitableなメソッド?¶ • GetAwaiter() という名前のメソッド が定義された型 • そんな型を返すメソッド • Duck-Typing

Slide 11

Slide 11 text

1.10. awaitableなメソッド?¶ class Awatable { ... public Awaiter GetAwaiter() { } ... }

Slide 12

Slide 12 text

1.11. Awaiter?¶ • INotifyCompletionインターフェースを実装 • IsCompletedプロパティ • GetResultメソッド • まぁ...そんな型

Slide 13

Slide 13 text

1.12. Awaiter?¶ struct Awaiter : INotifyCompletion { public bool IsCompleted { get; } public T GetResult() {} // from INotifyCompletion void OnCompleted(Action k) {} }

Slide 14

Slide 14 text

1.13. 標準では...¶ System.Threading.Tasks • Task • Task

Slide 15

Slide 15 text

1.14. 標準では...¶ Windows.Foundation • IAsyncAction • IAsyncOperation • IAsyncActionWithProgress • IAsyncOperationWithProgress

Slide 16

Slide 16 text

1.15. 同期化¶ • await後に元のスレッドに戻ってくる • SynchronizationContextが有効 • ConfigureAwait(false)でも無効化

Slide 17

Slide 17 text

1.16. 同期化¶ • WinRTでは • WinRTSynchronizationContext

Slide 18

Slide 18 text

1.17. 同期化¶ 注意 • 直接Taskをインスタンス化した場合 • 同期化されません • STA前提のメソッドは死ぬ

Slide 19

Slide 19 text

1.18. async / awaitの内側¶ • コンパイラの中の人が • 継続渡し、状態遷移機械などの • 黒魔術を駆使して • 同期的な記述を非同期処理に変換

Slide 20

Slide 20 text

1.19. async / awaitの内側¶ ↓詳しくはココ http://blogs.msdn.com/b/ windowsappdev_ja/archive/ 2012/04/30/winrt-await.aspx

Slide 21

Slide 21 text

1.20. async / awaitでの苦労話¶ ココから本題

Slide 22

Slide 22 text

1.21. async / awaitでの苦労話¶ 1. 自前のTaskでawaitableなメソッド呼ぶ 結果 • 後続の実行前にTaskが完了する

Slide 23

Slide 23 text

1.22. async / awaitでの苦労話¶ await Task.Run(async () => { await FooAsync(); // ↓実行されない return await BarAsync(); });

Slide 24

Slide 24 text

1.23. async / awaitでの苦労話¶ どうする? • 解決方法なし • 直接作ったTask中の使用はあきらめましょう

Slide 25

Slide 25 text

1.24. async / awaitでの苦労話¶ 1. Linqと相性が悪い • Linq内でawaitすると • 戻り値がIEnumerable>

Slide 26

Slide 26 text

1.25. async / awaitでの苦労話¶ var r = sources.Select(async (s) => { return await BarAsync(s); });

Slide 27

Slide 27 text

1.26. async / awaitでの苦労話¶ • 値を取り出すために • foreachで回す必要があり

Slide 28

Slide 28 text

1.27. async / awaitでの苦労話¶ foreach (var task in results) { var result = await task; }

Slide 29

Slide 29 text

1.28. async / awaitでの苦労話¶ • よく訓練されたLinq使い • foreachを嫌う • Linqのうまみ半減

Slide 30

Slide 30 text

1.29. async / awaitでの苦労話¶ どうする? • Linqとawaitの併用はあきらめましょう

Slide 31

Slide 31 text

1.30. async / awaitでの苦労話¶ 1. Task.Wait()で死ねる • 同期コンテキスト有効で • 非同期処理連鎖の内側で呼ぶと • デッドロック

Slide 32

Slide 32 text

1.31. async / awaitでの苦労話¶ int Foo() { // ↓ 中でawaitするメソッド var task = BarAsync(); // ココでお見合い task.Wait(); return task.Result; }

Slide 33

Slide 33 text

1.32. async / awaitでの苦労話¶ • 同期化のために • なかのawaitが呼出し元を待つから ↓詳しくはココミテ http://blogs.msdn.com/b/pfxteam/ archive/2012/04/12/10293249.aspx

Slide 34

Slide 34 text

1.33. async / awaitでの苦労話¶ どうする? • あきらめま(ry

Slide 35

Slide 35 text

1.34. async / awaitでの苦労話¶ 1. ならばと、GetAwaiter().GetResult()で無理矢理呼ぶ

Slide 36

Slide 36 text

1.35. async / awaitでの苦労話¶ public async void Foo() { var task = BarAsync(); task.GetAwaiter().GetResult(); }

Slide 37

Slide 37 text

1.36. async / awaitでの苦労話¶ • 結果 • はい、デッドロック

Slide 38

Slide 38 text

1.37. async / awaitでの苦労話¶ どうする? • あ(ry

Slide 39

Slide 39 text

1.38. async / awaitでの苦労話¶ 1. Parallel.Foreachの中では使えない

Slide 40

Slide 40 text

1.39. async / awaitでの苦労話¶ Paralle.Foreach(list, async (x) => { await Foo(x); }); • すべてのアクション完了まで待ってほしいのに... • Task.Run同様、投げっ放しジャーマン

Slide 41

Slide 41 text

1.40. async / awaitでの苦労話¶ どうする? • 気合いで非同期版作って下さい ↓ココミテ http://blogs.msdn.com/b/pfxteam/ archive/2012/03/04/10277325.aspx

Slide 42

Slide 42 text

1.41. async / awaitでの苦労話¶ 1. ユニットテストしづらい • NUnitはasync / await未対応 • 何も考えず走らせると • テストが実行されず終了(CLI版)

Slide 43

Slide 43 text

1.42. async / awaitでの苦労話¶ どうする?

Slide 44

Slide 44 text

1.43. async / awaitでの苦労話¶ CUI Runnerの場合 • 同期化されていないので • Taskk.Wait() or GetAwaiter().GetResult()で待たせる

Slide 45

Slide 45 text

1.44. async / awaitでの苦労話¶ public void RunTest(Func inTest) { inTest.GetAwaiter().GetResult(); } [Test] public void _TestSomething() { RunTest(async () => { ... }); }

Slide 46

Slide 46 text

1.45. async / awaitでの苦労話¶ GUI Runnerの場合 • 自前の同期コンテキストを気合いで実装 • 割と安定してる • けどグローバルな存在なので、ほかでこっそり使われると死ねる

Slide 47

Slide 47 text

1.46. WinRTのハマりどころ¶ • Windowsストアアプリ作成のためのAPIセット • 非同期処理を多用 • async / awaitの知識必須

Slide 48

Slide 48 text

1.47. Windowsストアアプリ¶ • Met(検閲により削除)... • とか言うと • MSの方から来た人に手招きされるかもなので注意

Slide 49

Slide 49 text

1.48. WinRTでの苦労話¶ 1. Windows.Storage APIの使用 スレッドまたいで使用するとSIBOU ArgumentException 範囲外です • (何が?) COMException その他のエラー • (追跡不能)

Slide 50

Slide 50 text

1.49. WinRTでの苦労話¶ STAの制約っぽい • スレッドをまたがない • 再取得すると問題出ない

Slide 51

Slide 51 text

1.50. WinRTでの苦労話¶ 1. BackgroundTask • awaitすると • 戻ってくる前に完了状態になっちゃう

Slide 52

Slide 52 text

1.51. WinRTでの苦労話¶ どうする? • defferalを取得しておけば待ってくれる • GetDefferal() • 終わったらdefferalのComplete()呼ぶ

Slide 53

Slide 53 text

1.52. WinRTでの苦労話¶ 1. BackgroundTask スレッドの生成がアプリ本体とは異なる。 • メインスレッドがParallel#ForEachの中に入ってきた。

Slide 54

Slide 54 text

1.53. WinRTでの苦労話¶ 1. BackgroundTask • 中でTask.Wait()してしまっていたので • デッドロックした • /(^o^)\

Slide 55

Slide 55 text

1.54. まとめ¶ • C# 5.0で非同期処理が手軽に使えるようになった • けど...ハマりどころ多数 • まぁ、まるちすれっどですゆえ

Slide 56

Slide 56 text

1.55. まとめ¶ (・ワ・)