$30 off During Our Annual Pro Sale. View Details »

パターンでわかる!​ ​ .NET Coreの​ 非同期処理

パターンでわかる!​ ​ .NET Coreの​ 非同期処理

.NET Core 3.0時点での、非同期処理にまつわる全てのキーポイントを解説しました。

発表動画もYouTubeで公開しているので、合わせて参照してください:
https://www.youtube.com/watch?v=YF7n8YqEfCg

.NET非同期処理のおさらい​
* 概要と基本的な書き方​
* コンソールアプリケーションとGUIアプリケーションとの違い​
* お勧めしたいプラクティスとアンチパターン​

.NET非同期処理の現実と設計​
* 現実的なアプリケーションへの適用と課題​
* 追加された非同期処理の要素​
* 足りなかったもの・足りていると勘違いされていたもの​

------

.NET Conf 2019 meetup in AICHI
https://centerclr.connpass.com/event/143949/

Kouji Matsui
PRO

October 05, 2019
Tweet

More Decks by Kouji Matsui

Other Decks in Programming

Transcript

  1. パターンでわかる!
    .NET Coreの
    非同期処理
    2019.10.5 .NET CONF 2019 MEETUP IN AICHI
    - KOUJI MATSUI (@KOZY_KEKYO, @KEKYO2)

    View Slide

  2. Kouji Matsui – kozy, kekyo
    • NAGOYA city, AICHI pref., JP
    • Twitter – @kozy_kekyo, @kekyo2 /
    Facebook
    • Self employed (I’m looking for a job)
    • Microsoft Most Valuable Professional VS
    and DevTech 2015-
    • Certified Scrum master / Scrum product
    owner
    • Center CLR organizer.
    • .NET/C#/F#/IL/metaprogramming or like…
    • Bike rider

    View Slide

  3. Agenda
    .NET Core 3とC# 8.0で完成した.NET非同期処理
    .NET非同期処理のおさらい
    .NET非同期処理の現実と設計
    追加された非同期処理の要素

    View Slide

  4. What’s new features
    本日は、以下のことをお話します:
    .NET非同期処理のおさらい
    ◦ 概要と基本的な書き方
    ◦ コンソールアプリケーションとGUIアプリケーションとの違い
    ◦ お勧めしたいプラクティスとアンチパターン
    .NET非同期処理の現実と設計
    ◦ 現実的なアプリケーションへの適用と課題
    追加された非同期処理の要素
    ◦ 足りなかったもの・足りていると勘違いされていたもの

    View Slide

  5. What’s new features
    .NET Core 3とC# 8.0で、.NETの非同期処理はついに完成
    Task
    async-await
    ValueTask
    IAsyncEnumerable
    Awaitable foreach


    View Slide

  6. What’s new features
    .NET Core 3とC# 8.0で、.NETの非同期処理はついに完成
    Task
    async-await
    ValueTask
    IAsyncEnumerable
    Awaitable foreach .NET Core 3


    View Slide

  7. What’s new features
    .NET Core 3とC# 8.0で、.NETの非同期処理はついに完成
    Task
    async-await
    ValueTask
    IAsyncEnumerable
    Awaitable foreach
    非同期処理記述の
    容易さの壁 (1)
    非同期処理記述の
    容易さの壁 (2)


    今日は、壁より前の話や内部の話はしません。
    なぜ非同期で処理を行う必要があるのかについては、
    「Async訪ねて3000里」を参照

    View Slide

  8. .NET非同期処理のおさらい
    非同期処理、ポイントを押さえれば難しくありません。なの
    に、正しく書けてない、場合によっては危険なコードが散見さ
    れます。
    .NET Core 3とC# 8.0で劇的に便利になったので、そろそろマス
    ターしちゃいましょう!
    非同期処理を記述する際に遭遇する問題のパターンと、解決
    の方策を示します。

    View Slide

  9. 基本的な操作(1) – ファイルアクセス
    コンソールアプリケーションで非同期処理してみる
    ファイル出力を非同期処理で書いてみる

    View Slide

  10. 基本的な操作(1) – ファイルアクセス
    任意のデータを
    ファイルに出力する
    FileAccessWithAsync.csproj

    View Slide

  11. 基本的な操作(1) – ファイルアクセス
    注目すべきポイント

    View Slide

  12. 基本的な操作(1) – ファイルアクセス
    I/O操作はすべて(Taskを返却する)
    非同期対応メソッドを使用してawaitする

    View Slide

  13. 基本的な操作(1) – ファイルアクセス
    メソッド中でawaitを使う場合は、メソッド
    宣言でasyncとマークし、Taskを返す

    View Slide

  14. 基本的な操作(1) – ファイルアクセス
    C# 7.1から、Mainメソッドが
    Taskを返せるようになった

    View Slide

  15. 基本的な操作(1) – ファイルアクセス
    FileStreamクラス固有:
    falseまたは省略した場合は、
    非同期I/Oはワーカースレッド
    によるシミュレート

    View Slide

  16. ファイルアクセス Appendix
    ファイルアクセスでよく使われる
    Fileクラスのメソッド

    View Slide

  17. ファイルアクセス Appendix
    Task.Runを使って、ワーカースレッドでシ
    ミュレートするのと同じなので、非効率

    View Slide

  18. ファイルアクセス Appendix
    Mainメソッドの対処方法:
    C#7.0以前を使う場合は、このように非同期
    メソッドにバイパスする
    (但しコンソールアプリケーションのみ)

    View Slide

  19. 基本的な操作(2) – 独立した非同期処理
    非同期処理を行うメソッドを作る
    非同期処理を行うクラスライブラリを作る

    View Slide

  20. 基本的な操作(2) – 独立した非同期処理
    指定されたURLからJSON形式
    のデータをダウンロードして
    パースするライブラリ
    IndependentLibrary.csproj

    View Slide

  21. 基本的な操作(2) – 独立した非同期処理
    注目すべきポイント

    View Slide

  22. 基本的な操作(2) – 独立した非同期処理
    メソッド中でawaitを使う場合は、メソッド
    宣言でasyncとマークする

    View Slide

  23. 基本的な操作(2) – 独立した非同期処理
    メソッド内部で非同期処理を行い、結果
    を値として返すので、Task型とする

    View Slide

  24. 基本的な操作(2) – 独立した非同期処理
    Task又はTaskを返す場合に、
    メソッド名末尾にAsyncをつける
    (必須ではないが強く推奨)
    `async`とマークした場合、
    ではないことに注意

    View Slide

  25. 独立した非同期処理 Appendix
    末尾にAsyncとつける推奨について:
    1. 末尾にAsyncとついていれば、使用者が非同期処理対応の
    メソッドを容易に見分けられる(インテリセンスを活用
    する意味でも重要)
    2. よく見られる誤ったプラクティス: `async`とマークした場
    合に、メソッド名末尾にAsyncと付ける…
    → TaskやTaskを返した場合に、末尾にAsyncを付ける
    Streamクラスのメンバー一覧

    View Slide

  26. 独立した非同期処理 Appendix
    抽象度の高いインターフェイス定義。
    メソッドは値を返さない
    誤ったプラクティスと考える理由
    InterestingService.csproj

    View Slide

  27. 独立した非同期処理 Appendix
    共通のインターフェイスメソッドを
    実装し、ログを出力する

    View Slide

  28. 独立した非同期処理 Appendix
    ログ出力の負荷を軽減するため、バックグラ
    ウンドで出力するように改良。
    呼び出し元はログがバックグラウンドで出力
    されているかどうかを気にしなくても良い
    (=通知手段がないので出来ない)

    View Slide

  29. 独立した非同期処理 Appendix
    ウェブサービスに
    アクセスする実装

    View Slide

  30. 独立した非同期処理 Appendix
    メソッド中でawaitを使う場合は、メソッド
    宣言でasyncとマークする

    View Slide

  31. 独立した非同期処理 Appendix
    `async`とマークしているが、
    末尾にAsyncと命名できない…
    `async`のマークは、内部でawaitキーワードを使っ
    たかどうかであって、非同期処理をするかどうか
    (インターフェイスの規定)とは関係がない

    View Slide

  32. 独立した非同期処理 Appendix
    このメソッドは、同期的に値を返さない。
    内部の処理は同期的に行われているかもしれないし、非同期的に行われて
    いるかもしれない(わからない)。
    非同期的に行われているのであれば、その結果を知ることは出来ない。
    このインターフェイスメソッドの
    真の意味

    View Slide

  33. WPFやWinFormsで非同期処理をおこなう
    基本的な操作(3) – GUI内での操作

    View Slide

  34. 基本的な操作(3) – GUI内での操作
    ボタンが押されたら
    ファイルを生成する
    WPFアプリケーション
    WpfAppWithAsync.csproj

    View Slide

  35. 基本的な操作(3) – GUI内での操作
    注目すべきポイント

    View Slide

  36. 基本的な操作(3) – GUI内での操作
    メソッド中でawaitを使う場合は、メソッド
    宣言でasyncとマークする

    View Slide

  37. 基本的な操作(3) – GUI内での操作
    イベントハンドラの戻り値をTaskにする
    ことは出来ない。その場合、voidだけが
    非同期メソッドとして許される

    View Slide

  38. 基本的な操作(3) – GUI内での操作
    イベントハンドラの戻り値を
    Taskにすることは出来ない

    View Slide

  39. 基本的な操作(3) – GUI内での操作
    メソッド名末尾にAsyncと付けるプラクティスの背景と
    同じことが、イベントハンドラについても言えます
    このメソッドは、同期的に値を返さない。
    内部の処理は同期的に行われているかもしれないし、非同期的に行われて
    いるかもしれない(わからない)。
    非同期的に行われているのであれば、その結果を知ることは出来ない。

    View Slide

  40. 基本的な操作 Appendix
    戻り値の型 直接的な意味 処理の詳細
    Task 非同期処理を行いT型の結果
    を返す
    非同期処理を行い、処理完了後に
    結果を値として返す
    Task 非同期処理を行い結果を返さ
    ない
    非同期処理を行い、処理完了後に
    完了したことだけが通知される
    T 同期処理でT型の結果を返す、
    又は結果を返しつつ非同期処
    理の状態を返さない
    通常の値を返すメソッド。しかし
    場合によっては非同期処理が走っ
    ているかもしれない
    void 同期処理で結果を返さない、
    又は非同期処理で状態も結果
    も返さない
    通常の値を返さないメソッド。し
    かし場合によっては非同期処理が
    走っているかもしれない

    View Slide

  41. .NET非同期処理の現実と設計
    ここまでで、基本的な非同期処理の実装の方法を再確認して
    きました。
    この節では、現実に発生しうる問題について、もう少し掘り
    下げます。

    View Slide

  42. 現実と設計(1) – 競合と回避
    非同期処理中に競合条件が発生する場合、その回避方法を確
    認します。
    回避方法は非同期処理特有で、従来の手法が通用しないこと
    を知ってください。

    View Slide

  43. 現実と設計(1) – 競合と回避
    100回実行(つまり、100回インクリメント
    されるはず)
    RaceConditionWithAsync.csproj
    ファイル内の数値文字列を
    インクリメントして
    ファイルに書き戻す

    View Slide

  44. 現実と設計(1) – 競合と回避
    非同期処理
    Task.WhenAllで、100個のTaskを
    並列実行することになる

    View Slide

  45. 現実と設計(1) – 競合と回避
    100にならない…
    → 排他処理を何もしていないので、
    競合が発生している

    View Slide

  46. 現実と設計(1) – 競合と回避
    awaitを使っていると、lockステートメントで
    モニターロックは出来ない
    (C#コンパイルエラー)
    モニターロック(ミューテックス)で
    競合を回避したい、が…

    View Slide

  47. 現実と設計(1) – 競合と回避
    モニターロックを手動で構成…

    View Slide

  48. 現実と設計(1) – 競合と回避
    SynchronizationLockException:
    モニターロックが異なるスレッドで
    解除されようとしている

    View Slide

  49. 現実と設計(1) – 競合と回避
    1 2 3 4
    await
    await
    await
    継続処理用ワーカースレッド群
    (スレッドプールから取得)
    awaitの前後で、処理スレッドが変わる
    (かもしれない)

    View Slide

  50. 現実と設計(1) – 競合と回避
    1 2 3 4
    await
    await
    await
    モニターロック取得スレッド
    モニターロック解放スレッド
    スレッドが一致していないので例外が発生

    View Slide

  51. 現実と設計(1) – 競合と回避
    つまりこの方法は根本的にダメ。
    C#コンパイラが親切にlockを
    使わせないようにしている

    View Slide

  52. 現実と設計(1) – 競合と回避
    NuGetパッケージ: Nito.AsyncEx
    Stephen Cleary氏の作品
    ほぼlockステートメントと同じように使えて、
    非同期処理対応(排他待機をawait出来る)

    View Slide

  53. 現実と設計(1) – 競合と回避
    .NET標準(ハードウェイト) Nito.AsyncEx(非同期処理対応)
    Monitor, lockステートメント AsyncLock, AsyncMonitor
    Mutex AsyncLock
    Semaphore AsyncSemaphore
    ManualResetEvent AsyncManualResetEvent
    AutoResetEvent AsyncAutoResetEvent
    ReaderWriterLock AsyncReaderWriterLock
    しかし、そもそも排他制御を行って
    いると、オーバーヘッドでパフォー
    マンスが上がらないと言う問題があ
    ります(重要)
    他にもSlim版(SemaphoreSlimなど)がありますが、ハードウェ
    イトのみないし、部分的に非同期に対応しているのみなので、
    非同期処理には推奨しません
    Nito.AsyncExを使うと、非同期処理
    で安全に排他制御できます

    View Slide

  54. 現実と設計(2) – GUIとスレッド
    GUIアプリケーションで非同期処理を行う場合は、コンソー
    ルアプリケーションとは異なる動きをします。
    なぜ、GUIアプリケーションでは非同期処理が強く推奨され
    るのか、について掘り下げます。

    View Slide

  55. 現実と設計(2) – GUIとスレッド
    ボタンが押されたら
    ファイル内の数値文字列を
    インクリメントする
    WPFアプリケーション
    WpfAppRaceConditionWithAsync.csproj

    View Slide

  56. 現実と設計(2) – GUIとスレッド
    GUIアプリケーションでも
    非同期処理は普通に使える

    View Slide

  57. 現実と設計(2) – GUIとスレッド
    1 2 3 4
    await
    await
    await
    ①がメインスレッド
    その他はワーカースレッド (?)
    ワーカースレッドで実行され
    たら例外で死ぬのでは?

    View Slide

  58. 現実と設計(2) – GUIとスレッド
    こう書くべき??
    いいえ、Dispatcherを使ってメインスレッドに
    明示的に転送しなくても問題ありません!
    なぜならば…

    View Slide

  59. 現実と設計(2) – GUIとスレッド
    1
    await
    await
    await
    実際にはメインスレッドしか
    使われないからです!!
    メインスレッドで実行される

    View Slide

  60. 現実と設計(2) – GUIとスレッド
    1
    await
    await
    await
    根本的な疑問「なぜ非同期処理をGUIアプリケー
    ションに適用すると、ユーザーインターフェイス
    が固まらないのですか?」

    View Slide

  61. 現実と設計(2) – GUIとスレッド
    1
    await
    await
    await
    awaitすると、メインスレッドは一時的にApplication.Run()の内部に戻ること
    が出来て、非同期処理が完了するまでは、別のイベントを処理できます
    (ボタンクリックイベント処理や、描画処理など)
    Application.Run()
    OnClick
    OnRender
    Application.Run()
    Application.Run()

    View Slide

  62. 現実と設計(2) – GUIとスレッド
    1 2 3 4
    await
    await
    await
    await後の処理を
    「継続処理」と呼びます
    継続処理 (1)
    継続処理 (2)
    継続処理 (3)
    コンソールアプリケーションと
    どう違うのか?

    View Slide

  63. 現実と設計(2) – GUIとスレッド
    1 2 3 4
    await
    await
    await
    デフォルトでは、継続処理の実行には、必ず
    ワーカースレッドが割り当てられます
    (メインスレッドとの区別はほとんどない)
    継続処理 (1)
    継続処理 (2)
    継続処理 (3)

    View Slide

  64. 現実と設計(2) – GUIとスレッド
    継続処理 (1)
    継続処理 (2)
    継続処理 (3)
    1
    await
    await
    await
    WPFやWinFormsでは、継続処理の実行に、
    常にメインスレッドが割り当てられます。
    この制御を行っているのが、
    SynchronizationContextの派生クラスです

    View Slide

  65. 現実と設計(2) – GUIとスレッド
    いつもメインスレッドが継続処理を実行
    しているとしたら、こうやって書いてい
    るのと同じようなものでは?
    (つまり、遅いのでは?)
    (厳密には違うのですが)
    パフォーマンスの懸念がある点では
    その通りです…

    View Slide

  66. 現実と設計(2) – GUIとスレッド
    そこで、ConfigureAwait()を使います
    await
    await
    await
    1

    View Slide

  67. 現実と設計(2) – GUIとスレッド
    1 2 3 4
    await
    await
    await
    WPFデフォルトの挙動を無視し、以降は
    ワーカースレッドを割り当てるようになる
    もはや必ずワーカースレッドで実行されるので、
    UIにアクセスする場合は、Dispatcherを使う必要があります

    View Slide

  68. 現実と設計(2) – GUIとスレッド
    1 2 3 4
    await
    await
    await
    ワーカースレッド間の転送は比較的
    低コストなので、この間の継続処理
    は実行効率が高くなる
    ここはメインスレッドに
    転送するコストが高い

    View Slide

  69. 現実と設計(3) – 頻出パターン
    非同期処理には、特有のパターンがあります。
    うまくこのパターンに当てはめれば、高効率かつ劇的に簡単
    に実装できるようになります。
    競合と排他を避けるためにも、このパターンに乗せてくださ
    い。

    View Slide

  70. 現実と設計(3) – 頻出パターン
    XMLデータをダウンロードして
    パースする
    非同期パース対応(.NET Core 3から)
    AggregateThingsWithAsync.csproj

    View Slide

  71. 現実と設計(3) – 頻出パターン
    気象庁防災情報
    XMLフォーマット形式電文
    (中身はAtomフィード)

    View Slide

  72. 現実と設計(3) – 頻出パターン
    Atomフィードのエントリn個を

    View Slide

  73. 現実と設計(3) – 頻出パターン
    ダウンロードしてパースし、

    View Slide

  74. 現実と設計(3) – 頻出パターン
    コメントを抽出する

    View Slide

  75. 現実と設計(3) – 頻出パターン
    2重ループ

    View Slide

  76. 現実と設計(3) – 頻出パターン
    ループといえばLINQ

    View Slide

  77. 現実と設計(3) – 頻出パターン
    最初のAtomフィードの
    ダウンロードは同じ

    View Slide

  78. 現実と設計(3) – 頻出パターン
    内側のループに注目して、Task
    を返すメソッドやラムダ式に
    する

    View Slide

  79. 現実と設計(3) – 頻出パターン
    非同期ラムダブロック: Task

    View Slide

  80. 現実と設計(3) – 頻出パターン
    XML LINQ

    View Slide

  81. 現実と設計(3) – 頻出パターン
    Task.WhenAllで、複数のTaskを一度に
    非同期待機する

    View Slide

  82. 現実と設計(3) – 頻出パターン
    並列実行され、
    やがて結果がまとめて返される

    View Slide

  83. 現実と設計(3) – 頻出パターン
    結果を全て出力

    View Slide

  84. 現実と設計(3) – 頻出パターン
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    動きを振り返ってみます

    View Slide

  85. 現実と設計(3) – 頻出パターン
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    foreachの2重ループ
    逐次実行
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    ChattyなUIへのアクセス
    (GUIの場合、ボトルネックになりうる)

    View Slide

  86. 現実と設計(3) – 頻出パターン
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    WriteLine

    View Slide

  87. 現実と設計(3) – 頻出パターン
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    WriteLine
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    WriteLine
    LINQでそれぞれの
    Textを集約
    Task.WhenAllで
    全部の非同期
    を集約
    Task
    Task
    Task
    Task

    View Slide

  88. 現実と設計(3) – 頻出パターン
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    WriteLine
    Task
    Task
    Task
    Task
    内側の処理をそれぞれ独立させて、
    相互に依存しないようにします
    並列実行性が高まります(安全)
    →モデル設計 M-(V-VM)

    View Slide

  89. 現実と設計(3) – 頻出パターン
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    WriteLine
    全部の非同期処理を集約
    するだけ
    全部集約されてから、UIにアクセス
    裏を返すと全部集約されないと
    UIにアクセスできない
    (トレードオフ)

    View Slide

  90. 現実と設計 Appendix
    補足

    View Slide

  91. 現実と設計 Appendix
    ハードウェイト(ハードブロック)
    ◦ スレッドを(WindowsやLinuxなどのOSカーネルの機能で)一時停止させること。非同
    期処理に対して、非互換。例外やデッドロックを引き起こす可能性があります。
    ◦ Thread.Sleep(), Thread.Join()
    ◦ Monitor.Enter()
    ◦ WaitHandle.WaitOne(), WaitHandle.WaitAny(), WaitHandle.WaitAll()
    ◦ Task.Wait(), Task.Result
    いつも言っていますが、ここまでの解説で、
    上記は一切使っていません。
    「Task.Wait(), Task.Resultは使いません・使えません・
    使う必要がありません!!」

    View Slide

  92. 現実と設計 Appendix
    非同期処理(待機可能・Awaitable)
    ◦ 実行単位をスレッドではなく、抽象的なタスクとみなし、タスクを一
    時停止させる。タスクを実行するのはスレッドですが、特定のタスク
    がいつも特定のスレッドで実行されるとは限りません。
    ◦ Task.Delay()
    ◦ Stream.ReadAsync(), Stream.WriteAsync()
    ◦ TextReader.ReadLineAsync(), TextWriter.WriteLineAsync()
    ◦ HttpClient.GetAsync(), HttpClient.PostAsync()
    上記は全てTask又はTaskを返します。

    View Slide

  93. 現実と設計 Appendix
    ハードウェイトを行うメソッドを非同期処理と混ぜて安全に
    (かつパフォーマンスを最大化して)使用するのは、トップレ
    ベルの技術者でも非常に困難です(あるいは不可能)。
    つまり、やりたくなったら
    何かが間違っています
    いやでも今自分が遭遇している問題は、当てはまらない
    レアケースだから… と言っている問題も間違いです

    View Slide

  94. 追加された非同期処理の要素
    ValueTaskはTaskのメモリ確保コストを削減します。
    非同期イテレーターインターフェイスが、標準定義され、非
    同期イテレーターを列挙する構文・列挙可能にする構文が、
    C#8.0で追加されました。
    非同期イテレーターを活用すれば、モデル実装がより簡単に
    なります。

    View Slide

  95. 追加された非同期処理の要素(1) – ValueTask
    ValueTaskをTaskの代わりに使っていきたい動機について。
    なぜ効率が良いのか?

    View Slide

  96. 追加された要素(1) – ValueTask
    ValueTaskはTaskの構造体版

    View Slide

  97. 追加された要素(1) – ValueTask
    AggregateThingsWithAsync.csproj

    View Slide

  98. 追加された要素(1) – ValueTask
    1. Taskで宣言していたところを機械的にValueTaskに置
    き換え出来る。
    2. Taskで出来ることはValueTaskでもだいたいできる。
    3. ValueTask.AsTask()を呼び出せばTaskに変換できる。
    ValueTask.WhenAllはないが、Taskを
    返す非同期処理を混ぜて使える

    View Slide

  99. 何が嬉しいの?
    ◦ もし、非同期処理がその場で完了した場合(たとえば、ReadLineAsync()
    の読み取りがバッファリングされていたデータなどで直ぐに返せるな
    ど)、TaskだとTaskクラスのインスタンス生成にコストが掛かるけど、
    ValueTaskならほとんどコストがかからない(構造体だから)
    ◦ 非同期待機が実際に発生する場合に、初めて内部でTaskのインスタンス
    が生成される。
    追加された要素(1) – ValueTask

    View Slide

  100. つまり?
    async-await構文でawaitしまくるコードを書いた場合:
    ◦ Taskだとawaitの度にTaskのインスタンスが生成されるので、効率が悪い
    ◦ ValueTaskだと結果が直ぐに得られる場合は、インスタンス生成コスト
    が発生しない
    追加された要素(1) – ValueTask
    注意: ValueTaskを使う場合は、
    System.Threading.Tasks.Extensions
    パッケージを追加する必要があります

    View Slide

  101. 追加された要素(2) – 非同期イテレーター
    Task.WhenAllが配列を返す件。
    yield returnを非同期処理と組み合わせて使えます。

    View Slide

  102. 追加された要素(2) – 非同期イテレーター
    データ群の処理方法
    foreachによる逐次処理 Task.WhenAllによる集約処理
    メリット 従来のループ手法をそのまま適用
    できる。
    出力やUIへの反映が逐次実行され
    る(インタラクティブ性・スト
    リーミング)
    集合演算に根付く考え方に適合する
    なら、非常に直感的で高レベルな記
    述が可能(LINQ)。
    モデル設計として独立させやすい。
    並列実行される。
    デメリット 並列処理がやりにくい、あるいは
    そのままでは出来ない。
    モデル設計として処理を完全に分
    離することが難しい。
    全てのデータが集約されてからしか、
    結果にアクセスできない(配列)。

    View Slide

  103. 追加された要素(2) – 非同期イテレーター
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    WriteLine
    Text
    Text
    Text
    Text
    Text
    Text
    Text

    View Slide

  104. 追加された要素(2) – 非同期イテレーター
    entry Text
    Text
    Text
    entry Text
    Text
    entry Text
    Text
    Text
    Text
    entry Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    Text
    WriteLine
    この部分を、データが得られたところ
    から逐次実行できないものか…

    View Slide

  105. AggregateThingsWithAsync.csproj
    追加された要素(2) – 非同期イテレーター

    View Slide

  106. 追加された要素(2) – 非同期イテレーター
    IAsyncEnumerableで、列挙可能で
    かつ非同期処理を実現できる

    View Slide

  107. 追加された要素(2) – 非同期イテレーター
    普通にawaitできる

    View Slide

  108. 追加された要素(2) – 非同期イテレーター
    IEnumerableと同じように、
    yield returnで要素を返せる

    View Slide

  109. 追加された要素(2) – 非同期イテレーター
    実装の詳細が外部に漏れていない、
    真に独立したモデルメソッドを実現

    View Slide

  110. 追加された要素(2) – 非同期イテレーター
    非同期イテレーターは、
    新しいawait foreachという構文で、
    非同期的に列挙できる

    View Slide

  111. 追加された要素(2) – 非同期イテレーター
    awaitを付けたからasyncを忘れずに

    View Slide

  112. 追加された要素(2) – 非同期イテレーター
    非同期イテレーターとは?
    ValueTaskを使う。
    つまり、連続してすぐに値を返せる場合は、
    Taskよりもコストが低い

    View Slide

  113. 追加された要素(2) – 非同期イテレーター
    foreachによる逐次処理 Task.WhenAllによる集約処理 非同期イテレータを使う処理
    メリット 従来のループ手法をそのま
    ま適用できる。
    出力やUIへの反映が逐次実
    行される(インタラクティ
    ブ性・ストリーミング)
    集合演算に根付く考え方に適
    合するなら、非常に直感的で
    高レベルな記述が可能
    (LINQ)。
    モデル設計として独立させや
    すい。
    並列実行される。
    従来のループ手法をそのまま適
    用できる。
    出力やUIへの反映が逐次実行さ
    れる(インタラクティブ性・ス
    トリーミング)
    モデル設計として独立させやす
    い。
    デメリット 並列処理がやりにくい、あ
    るいはそのままでは出来な
    い。
    モデル設計として処理を完
    全に分離することが難しい。
    全てのデータが集約されてか
    らしか、結果にアクセスでき
    ない(配列)。
    並列処理がやりにくい、あるい
    はそのままでは出来ない。

    View Slide

  114. Any question? Thank you for join!!
    スライドのコードは全てここにあります:
    https://github.com/kekyo/dotnetconf2019

    View Slide

  115. Any question? Thank you for join!!
    https://github.com/kekyo/dotnetconf2019

    View Slide