SlideShare a Scribd company logo
1 of 65
An other world
awaits you
避けては通れない非同期処理
A n   o t h e r     w o r l d        a w a i t s      y o u

別世界があなたを待っています


                                                      C# 5.0
                                        C# 4.0
                                                      • Async
                            C# 3.0      • Dynamic
                            • LINQ
               C# 2.0                                    これ

               • Generics
      C# 1.0                         asynchrony
      • Managed                      非同期

                                                  ※VB 7~11の歴史でもある
A n   o t h e r   w o r l d   a w a i t s   y o u

別世界があなたを待っています
 こんな世界
   フリーズしない世界
   バッテリーの持ちがいい世界
                                             C# 5.0
                                             • Async
 “今までも、俺ならできたよ”
   訓練された人しかできない
   訓練された人でも超大変
   見合ったコストでは“できない”
アジェンダ
 非同期処理の裏側
 いろいろなタイプの非同期処理
 Windows 8/WinRT/.NET 4.5時代の非同期処理
async/await
前ふり
まず初めにC# 5.0/VB 11の強力さを
C# 5.0
 非同期処理                最大の売り
    async/await       本日の主役

 Caller Info
    CallerFilePath/CallerLineNumber/CallerMemberName属性
 細かい仕様変更/バグ修正
    foreach変数
    オーバーロード解決
    名前付き引数の評価順序




                       http://ufcpp.net/study/csharp/ap_ver5.html
                       http://msdn.microsoft.com/en-us/library/hh678682.aspx
C# 5.0のasync/await
 Taskクラス
 async修飾子
 await演算子
async Task<string> GetAsync(string url)
{
    var client = new HttpClient();
    var res = await client.GetAsync(url);
    var content = await res.Content.ReadAsStringAsync();
    return content;
}


                 同期処理の場合と
               ほぼ同じフローで非同期処理
await

 t.Wait();

 • wait for t: 類義語はstay(とどまる)
 • スレッドを止めて待つ

 await t;

 • await t: 類義語はextpect(期待する)
 • スレッドを止めずにコールバックを待つ
        重要: ちゃんと非同期
もう少し複雑な例
  複数の確認ダイアログ表示
        確認フロー


ゲームで       確認 1   Yes   レア アイテムですよ?
アイテムを     チェック
                                      結果表示
合成します
         No




           確認 2   Yes   合成強化済みですよ?
          チェック

         No




                  Yes
           確認 3         もう強化限界ですよ?
          チェック

         No
同期
if (this.Check1.IsChecked ?? false)
{
    var result = Dialog.ShowDialog("確認 1", "1つ目の確認作業");
    if (!result) return false;
}

if (this.Check2.IsChecked ?? false)
{
    var result = Dialog.ShowDialog("確認 2", "2つ目の確認作業");
    if (!result) return false;
}

if (this.Check3.IsChecked ?? false)
{
    var result = Dialog.ShowDialog("確認 3", "3つ目の確認作業");
    if (!result) return false;
}

return true;
非同期(旧)
if (this.Check1.IsChecked ?? false)




                                                                                     画面に収まるように
{
      Dialog.BeginShowDialog("確認 1", "1つ目の確認作業", result =>
      {
             if (!result)
             {
                    onComplete(false);




                                                                                      フォント サイズ調整
                    return;
             }

           if (this.Check2.IsChecked ?? false)
           {
                  Dialog.BeginShowDialog("確認 2", "2つ目の確認作業", result2 =>
                  {




                                                                                      4ptです
                        if (!result2)
                        {
                               onComplete(false);
                               return;
                        }

                      if (this.Check3.IsChecked ?? false)
                      {



                                                                                      ほんの84行ほど
                            Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 =>
                            {
                                   onComplete(result3);
                            });
                      }
                      else
                            onComplete(true);
                  });



                                                                                     ちなみに
           }
           else if (this.Check3.IsChecked ?? false)
           {
                  Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 =>
                  {
                        onComplete(result3);
                  });
           }



                                                                                      部分部分を関数化して多少は
           else
                  onComplete(true);
      });
}
else if (this.Check2.IsChecked ?? false)



                                                                                       整理できます
{
      Dialog.BeginShowDialog("確認 2", "2つ目の確認作業", result =>
      {
             if (!result)
             {
                    onComplete(false);
                    return;



                                                                                      ダイアログ3つだからまだこの
             }

           if (this.Check3.IsChecked ?? false)
           {
                  Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 =>



                                                                                       程度で済んでます
                  {
                        onComplete(result);
                  });
           }
           else
                  onComplete(true);
      });
}
else if (this.Check3.IsChecked ?? false)
{
      Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 =>
      {
             onComplete(result3);
      });
}
else
      onComplete(true);
非同期(C# 5.0)
if (this.Check1.IsChecked ?? false)
{
    var result = await Dialog.ShowDialogAsync("確認 1", "1つ目の確認作業");
    if (!result) return false;
}

if (this.Check2.IsChecked ?? false)
{
    var result = await Dialog.ShowDialogAsync("確認 2", "2つ目の確認作業");
    if (!result) return false;
}

if (this.Check3.IsChecked ?? false)
{
    var result = await Dialog.ShowDialogAsync("確認 3", "3つ目の確認作業");
    if (!result) return false;
}


                 • await演算子が増えただけ
return true;

                 • ダイアログが増えても平気
さて、今日の議題
こんなに便利なasync/awaitですが…
 中身はどうなってるの?
   非同期処理の実行インフラ
   awaitの実現方法
 awaitで万事解決?
   そうでもない
     並列処理
     イベント型の非同期
     データフロー
非同期処理の実行インフラ
スレッド プールとかI/Oとか同期コンテキストとか
非同期インフラ
 スレッド
 スレッド プール
 I/O完了ポート
 UIスレッドと同期コンテキスト
非同期インフラ
 スレッド
 スレッド プール
 I/O完了ポート
 UIスレッドと同期コンテキスト
スレッド
 非同期処理の最も低レイヤーな部分
                                   Worker1             Worker2
static void Main()
{
    var t = new Thread(Worker2);        CPUをシェア
    t.Start();                          定期的に切り替え
    Worker1();
}




    スレッドはプリエンプティブ※なマルチタスク
        ハードウェア タイマーを使って強制割り込み                              高負荷
        OSが特権的にスレッド切り替えを行う


                                             ※preemptive:   先売権のある
スレッド
 非同期処理の最も低レイヤーな部分
    ただし、高負荷

for (int i = 0; i < 1000; i++)
{
    var t = new Thread(Worker);
    t.Start();
}


 1000個の処理を同時実行


    細々とした大量の処理をこなすには向かない
        切り替え(コンテキスト スイッチ)のコストが高すぎる
スレッドを立てるコスト
 スレッドに紐づいたデータ
  カーネル ステート: 1kBくらい
  ローカル スタック: 1MBくらい
 イベント発生
  Thread Attached/Detachedイベント
スレッド切り替えコスト
 カーネル モードに移行
 レジスターの値を保存
 lockを獲得
 次に実行するスレッドの決定                               どれも結構時間が
 lockを解放                                     かかる処理

 スレッドの状態を入れ替え
 レジスターの値の復元
 カーネル モードから復帰

        ※ http://blogs.msdn.com/b/larryosterman/archive/2005/01/05/347314.aspx
スレッド切り替えコスト削減
 切り替えの原因
  一定時間経過
  待機ハンドル待ち(lock獲得とか)
  同期I/O待ち



 対策
  そもそもスレッドを立てない
                       自前実装大変
  lock-freeアルゴリズム利用   スレッドは直接使わない
  I/Oは非同期待ち
                        スレッド プール
                        I/O完了ポート
スレッド切り替えコスト削減
 切り替えの原因
   一定時間経過
 .NETでいうと
   待機ハンドル待ち(lock獲得とか)
  • Threadクラスは使わない
   同期I/O待ち
     .NET for Windowsストア アプリではついに削除された

   • Taskクラスを使う
 対策 3.5以前の場合はThraedPoolクラスやTimerクラス、
   .NET
    IAsyncResultインターフェイス
   そもそもスレッドを立てない
                              自前実装大変
   lock-freeアルゴリズム利用         スレッドは直接使わない
   I/Oは非同期待ち
                                スレッド プール
                                I/O完了ポート
非同期インフラ
 スレッド
 スレッド プール
 I/O完了ポート
 UIスレッドと同期コンテキスト
2種類のマルチタスク
 プリエンプティブ


 •   ハードウェア タイマーを使って強制割り込み
 •   OSが特権的にスレッド切り替えを行う
 •   利点: 公平(どんなタスクも等しくOSに制御奪われる)
 •   欠点: 高負荷(切り替えコストと使用リソース量)



 協調的※


 •   各タスクが責任を持って終了する
 •   1つのタスクが終わるまで次のタスクは始まらない
 •   利点: 低負荷
 •   欠点: 不公平(1タスクの裏切りが、全体をフリーズさせる)

                                   ※cooperative
スレッド プール
 スレッドを可能な限り使いまわす仕組み
  プリエンプティブなスレッド数本の上に
  協調的なタスク キューを用意
            スレッド プール
          キュー
                       数本のスレッド
新規タスク                   だけ用意
          タスク1

          タスク2
 タスクは一度
キューに溜める
           …




      空いているスレッドを探して実行
    (長時間空かない時だけ新規スレッド作成)
スレッド プールの性能的な工夫
 Work Stealing Queue
    lock-free実装なローカル キュー
       できる限りスレッド切り替えが起きないように


  グローバル             ローカル          ローカル
   キュー              キュー1          キュー2


                    スレッド1         スレッド2
         ①
         スレッドごとに
                            ②
         キューを持つ
                            ローカル キュー
                            が空のとき、
                            他のスレッドから
                            タスクを奪取
参考: lock-freeアルゴリズム
lockベース                                OS機能に頼った競合回避
lock (_sync)                          (カーネル モード移行あり)
{
    _value = SomeOperation(_value);
}

lock-free(interlockedベース)
                                         競合してたらやり直す
long oldValue1, oldValue2;            CPUのInterlocked命令※を利用
do
{
                                       競合頻度が低い時に高効率
    oldValue1 = _value;
    var newValue = SomeOperation(_value);
    oldValue2 = Interlocked.CompareExchange(
        ref _value, newValue, oldValue1);
}
while (oldValue1 != oldValue2);

            ※   アトミック性を保証したCPU命令。カーネル モード移行と比べるとだいぶ低不可
                interlocked: 連結した、連動した
非同期インフラ
 スレッド
 スレッド プール
 I/O完了ポート
 UIスレッドと同期コンテキスト
おさらい
 待機しちゃダメ
  × lock           何もしていないのにスレッド立てっぱなし
  × 同期I/O待ち        • スタック(1MB)取りっぱなし
                    • スレッド切り替えの誘発
  × Thread.Sleep   • スレッド プール上でも新しいスレッド
                      が立ってしまう



  ○ 非同期I/O
                          I/O完了ポート
  ○ タイマー(Task.Delay)
I/O
 Input/Output
    CPUと、CPUの外との入出力   待機しちゃダメ

    CPU内での計算と比べると、数ケタ遅い


                       ユーザー入力


                       通信
       CPU
                       ストレージ


                       ハードウェア タイマー
I/O完了ポート※
 I/Oを待たない
   コールバックを登録
   I/O完了後、スレッド プールで続きの処理を行う

       アプリ   あるスレッド               スレッド プール

                                  タスク1
コールバック登録後、       コールバック           タスク2
                   登録
すぐにスレッド上での




                                   …
   処理を終了

    I/O完了ポート
               I/O開始      I/O完了
   (OSカーネル内)

                 ハードウェア

                                       ※   I/O completion port
非同期APIを使いましょう
 同じ非同期処理でも

 Task.Run(() => req.GetResponse());

 • スレッド内で同期I/O
 • スレッド立てっぱなし

 req.GetResponseAsync();

 • I/O完了ポートを使って非同期I/O
 • コールバック登録後、スレッドを解放
Sleepもダメ(例: Sleep Sort)
 値に比例してSleepすればソートできるんじゃね?
   というネタ。
×スレッド立ててSleep
new Thread(_ => { Thread.Sleep(t); q.Enqueue(x); }).Start();

                             要素数分のスレッドが立つ
                             要素数×1MBのスタック確保
                                既定の設定だと、スレッド1,000個くらいで
                                Out of Memory


○タイマー利用+コールバック
Task.Delay(t).ContinueWith(_ => q.Enqueue(x));
ダメなものは最初から提供しない
 Windows 8世代のAPI
    50ミリ秒以上かかる可能性のあるAPIは
    非同期APIのみ提供
   WinRT
      ファイル操作、ネットワーク、グラフィック
      ランチャー、ダイアログ表示
   .NET 4.5で追加されたクラス
      HttpClientなど
   .NET for Windows ストア アプリ
      同期I/O APIやThreadクラス削除
非同期インフラ
 スレッド
 スレッド プール
 I/O完了ポート
 UIスレッドと同期コンテキスト
シングル スレッド必須
 スレッド安全なコードは高コスト


 いっそ、単一スレッド動作を前提に
  典型例はGUI
    C#/.NETに限らずたいていのGUIフレームワークはシングル
     スレッド動作
    低レイヤーAPI(DirectXとかOpenGLとか)も、1つのス
     レッドからしか扱えない
典型例: UIスレッド
 GUIは単一スレッド動作(UIスレッド)
  ユーザーからの入力受け付け
  画面の更新

         UIスレッド            他のスレッド
 ユーザー             グラフィック
 からの入力
             更新
                    OK




  処理中は             他のスレッドから
  応答不可              は更新不可
矛盾
           シングル スレッド推奨


          単一スレッドからしか
            UI更新できない
           OK



 そのスレッドを止める
   とUIフリーズ

  マルチ スレッド推奨
解決策
1. スレッド プールで重たい処理
2. UIスレッドに処理を戻してからUI更新
     UIスレッド                     他のスレッド
         Task.Run


                                            重たい処理
                    Dispatcher.Invoke


         更新
                      OK
                                戻す役割を担うのが
                                ディスパッチャー※


                                        ※   dispatcher: 配送者
ディスパッチャーの利用例
 WPFの場合※
 Task.Run(() =>
 {
     var result = HeavyWork();

       this.Dispatcher.Invoke(() =>
       {
           this.List.ItemsSource = result;
       });
 });


            • あまり意識したくないんだけども
            • 自動的にはやってくれないの?




                      ※   WPF、Silverlight、WinRT XAMLでそれぞれ書き方が少しずつ違う
自動化するにあたって
 処理の実行場所には文脈がある
  同期コンテキスト※と呼ぶ
 文脈         要件
 スレッド プール   どのスレッドで実行しててもいい
            実行効率最優先
 GUI        UIの更新は、UIスレッド上での実行
            が必要(ディスパッチャーを経由)
 Web API    どのスレッドで実行してもいい
            ただし、どのWebリクエストに対す
            る処理か、紐づけが必要

  適切な同期コンテキストを拾って実行する必要がある
                       ※   synchronization context
                           もちろん自作も可能
同期コンテキストの利用例
 var context = SynchronizationContext.Current;

 Task.Run(() =>
 {
     var result = HeavyWork();

       context.Post(r =>
       {
           this.List.ItemsSource = (IEnumerable<int>)r;
       }, result);
 });


                    文脈に応じた適切な実行方法をしてくれる
                    • WPFの場合はディスパッチャーへのPost

 あとは、ライブラリの中で自動的に同期コンテキ
  ストを拾ってくれれば万事解決…?
完全自動化無理でした
 スレッド プール中で同期コンテキストを拾うと、
  スレッド プールにしか返ってこない
  BackgroundWorkerのDoWork内で、別の
   BackgroundWorkerを作ると破綻
 必ずUIスレッドに処理を戻すと実行効率悪い
  ぎりぎりまでスレッド プール上で実行したい
 ネイティブ⇔.NETをまたげない
  WinRT XAML UIだと自動化無理



                  つまるところ、JavaScriptとかでそれができているのは
                  • 単一UIフレームワーク
                  • 実行効率度外視
なので、ある程度の自由を
 TaskSchdulerの選択
  既定動作(スレッド プール)
  Task.Run(() => HeavyWork())
      .ContinueWith(t =>
      {
          // スレッド プール上で実行される
      });

  同期コンテキストを拾う
  Task.Run(() => HeavyWork())
      .ContinueWith(t =>
      {
          // UI スレッド上で実行される
      }, TaskScheduler.FromCurrentSynchronizationContext());
ちなみに、awaitは
 既定で同期コンテキストを拾う
 既定動作(同期コンテキストを拾う)
 var result = await Task.Run(() => HeavyWork());


 挙動の変更(同期コンテキストを拾わない)
 var result = await Task.Run(() => HeavyWork())
     .ConfigureAwait(false);


 awaitした場所で同期コンテキストを拾う
  (ライブラリ内でなく)利用側で
  なので、ネイティブAPIを使っていても、ちゃんと拾える
いろいろな非同期
awaitも万能じゃない
awaitが解決するもの(1)
 1往復の、pull型非同期処理
 例: 前述のGUIでの非同期処理
     UIスレッド                          他のスレッド
             Task.Run


                                              重たい処理
             await




      var result = await Task.Run(() => HeavyWork());
awaitが解決するもの(2)
 1往復の、pull型非同期処理
 非同期I/Oも好例
     UIスレッド                         I/O完了ポート
             Task.Run
                                        I/O開始


             await
                                        I/O完了


      var result = await client.GetAsync();
awaitが解決するもの(3)
 1往復の、pull型非同期処理
 ダイアログ ウィンドウの表示なんかも
     UIスレッド                       GUIフレームワーク
             ダイアログ表示


     ユーザーがダイアログを操作
                                           OK

             await




      var result = await dialog.ShowAsync();




                     「UIスレッドの入れ子」みたいなことができないので、
                     一度処理を抜けなきゃいけない
awaitが解決しないもの
 並列処理
 イベント型非同期
 そもそも制御フローを書きにくいもの
並列処理
 マルチコアCPUを使いきりたい
  Parallelクラス
    Parallel.ForEach(data, x =>
    {
        // 並列に実行したい処理
    });


  Parallel LINQ
    var results = data.AsParallel()
        .Select(x => /* 並列に実行したい処理 */);
イベント型非同期処理
 複数件、push型
                                               処理側         発生側
      センサーAPI                                   ハンドラー登録

      サーバーからのpush通知
                                                 イベント発生
      ユーザー操作への応答も
       ある意味では非同期処理                               イベント発生



                                                 イベント発生
void Init()
{
    var sensor = Accelerometer.GetDefault();     イベント発生
    sensor.Shaken += sensor_Shaken;
}

void sensor_Shaken(
    Accelerometer sender,
    AccelerometerShakenEventArgs args)
{
    // イベント処理
}
WinRTのイベント型非同期処理
 ネイティブの向こう側で起こっていること
  同期コンテキストを自動的に拾えない
   void Init()
   {
       var sensor = Accelerometer.GetDefault();
       sensor.Shaken += sensor_Shaken;
   }

   void sensor_Shaken(
       Accelerometer sender,
       AccelerometerShakenEventArgs args)
   {
       // イベント処理          UIを更新するなら明示的な
   }
                          ディスパッチャー利用必須
WinRTのイベント型非同期処理
 Rx※とか使うのがいいかも


   var sensor = Accelerometer.GetDefault();

   Observable.FromEventPattern(sensor, "Shaken")
       .ObserveOn(SynchronizationContext.Current)
       .Subscribe(args =>
       {
           // イベント処理           TaskSchedulerと同じ感覚
       });




                        ※   Reactive Extensions
                            http://msdn.microsoft.com/en-us/data/gg577609
                            NuGet取得可・Windowsストア アプリでの利用可
制御フローを書きにくいもの
 awaitは、同期と同じように非同期を書ける


 そもそも同期でも書きにくいものは苦手
  2次元的なデータフローとか
  ステートマシンとか
制御フローを書きにくいもの
 WFとか
   http://msdn.microsoft.com/en-us/vstudio/aa663328
 TPL Dataflowとか
   http://msdn.microsoft.com/en-us/devlabs/gg585582.aspx
how to await
async/awaitの中身
非同期処理 おさらい
 待機しちゃダメ    task.ContinueWith(
                 コールバック);
  コールバック
  中断と再開      やるべきことは
             イテレーターと同じ
 同期コンテキスト
イテレーター
 中断と再開
                             class MethodEnumerator : IEnumerator<int>
                             {
                                 public int Current { get; private set; }
                                 private int _state = 0;

                                 public bool MoveNext()
                                 {
                                     switch (_state)
                                     {
                                         case 0:
 IEnumerable<int> Method()
 {                                       Current = 1;
                                         _state = 1;
     yield return 1;                     return true;
                                         case 1:

                                         Current = 2;
     yield return 2;                     _state = 2;
                                         return true;
                                         case 2:

                                         default:
 }                                       return false;
                                     }
                                 }
                             }
イテレーター
 中断と再開
                             class MethodEnumerator : IEnumerator<int>
                             {
                                 public int Current { get; private set; }
                                 private int _state = 0;

                                 public bool MoveNext()
                                 {
                                     switch (_state)
                                     {
                                         case 0:
                                                              状態の記録
 IEnumerable<int> Method()           Current = 1;
 {                                      Current = 1;
     yield return 1;
                                     _state = 1;
                                        _state = 1;
                                        return true;
                                     return 1:
                                        case true;      中断
                                     case 1: = 2;
                                        Current
                                        _state = 2;
                                        return true; 再開用のラベル
     yield return 2;
                                         case 2:

                                         default:
 }                                       return false;
                                     }
                                 }
                             }
awaitの展開結果(コンセプト)
 コンセプト的には イテレーター + ContinueWith

                                        状態の記録
                            _state = 1;
                            if (!task1.IsCompleted)
 async Task<int> Method()   {
 {                              task1.ContinueWith(a);
     var x = await task1;
     var y = await task2;       return;   中断
 }                          }
                            case 1:   再開用のラベル
                            var x = task1.Result;

                                      結果の受け取り
awaitの展開結果
 実際はもう少し複雑
  Awaiterというものを介していたり(Awaitableパターン)

  _state = 1;
  var awaiter1 = task1.GetAwaiter();
  if (!awaiter1.IsCompleted)
  {
      awaiter1.OnCompleted(a); • こいつが同期コンテキスト
      return;                    を拾い上げていたりする
  }                            • Awaiterを自作することで、
  case 1:                        awaitの挙動を変更可能
  var x = awaiter1.GetResult();
                          • Task以外もawait可能
逆に
 C# 5.0が使えなくても
   イテレーターを使ってawaitに似た非同期処理可能

                  そこそこめんどくさい

 C#が使えなくても
   IEnumerableを自作する程度の手間でawaitに似た(略

                  絶望的にめんどくさい
                  けど、性能は出ると思う
                  C#使わせてください。ほんとお願いします。
まとめ
まとめ
 スレッド(Thread)は直接使わない
    スレッド プール(Task)を使う
 await
    wait(stay)と違って、await(expect)
       イテレーター(中断と再開) + ContinueWith(コールバック)

    同期コンテキスト
 awaitにだって…できないことは…ある
    並列処理、イベント型非同期
    そもそも同期で書きにくいもの

More Related Content

What's hot

.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE).NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
Tusyoshi Matsuzaki
 
カスタムメモリマネージャと高速なメモリアロケータについて
カスタムメモリマネージャと高速なメモリアロケータについてカスタムメモリマネージャと高速なメモリアロケータについて
カスタムメモリマネージャと高速なメモリアロケータについて
alwei
 
オブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ
増田 亨
 
凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public
bonjin6770 Kurosawa
 

What's hot (20)

.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE).NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
 
Orange Cube 自社フレームワーク 2015/3
Orange Cube 自社フレームワーク 2015/3Orange Cube 自社フレームワーク 2015/3
Orange Cube 自社フレームワーク 2015/3
 
Unityでオニオンアーキテクチャ
UnityでオニオンアーキテクチャUnityでオニオンアーキテクチャ
Unityでオニオンアーキテクチャ
 
ObserverパターンからはじめるUniRx
ObserverパターンからはじめるUniRx ObserverパターンからはじめるUniRx
ObserverパターンからはじめるUniRx
 
C#/.NETがやっていること 第二版
C#/.NETがやっていること 第二版C#/.NETがやっていること 第二版
C#/.NETがやっていること 第二版
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っている
 
超便利! Unity Cloud Build の使い方
超便利! Unity Cloud Build の使い方超便利! Unity Cloud Build の使い方
超便利! Unity Cloud Build の使い方
 
カスタムメモリマネージャと高速なメモリアロケータについて
カスタムメモリマネージャと高速なメモリアロケータについてカスタムメモリマネージャと高速なメモリアロケータについて
カスタムメモリマネージャと高速なメモリアロケータについて
 
Go入門
Go入門Go入門
Go入門
 
Unity Cloud Buildの使い方
Unity Cloud Buildの使い方Unity Cloud Buildの使い方
Unity Cloud Buildの使い方
 
Msを16倍出し抜くwpf開発2回目
Msを16倍出し抜くwpf開発2回目Msを16倍出し抜くwpf開発2回目
Msを16倍出し抜くwpf開発2回目
 
自宅サーバ仮想化
自宅サーバ仮想化自宅サーバ仮想化
自宅サーバ仮想化
 
C# 8.0 null許容参照型
C# 8.0 null許容参照型C# 8.0 null許容参照型
C# 8.0 null許容参照型
 
オブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツオブジェクト指向の設計と実装の学び方のコツ
オブジェクト指向の設計と実装の学び方のコツ
 
マスター・オブ・Reflectパッケージ
マスター・オブ・Reflectパッケージマスター・オブ・Reflectパッケージ
マスター・オブ・Reflectパッケージ
 
【Unite 2017 Tokyo】「黒騎士と白の魔王」にみるC#で統一したサーバー/クライアント開発と現実的なUniRx使いこなし術
【Unite 2017 Tokyo】「黒騎士と白の魔王」にみるC#で統一したサーバー/クライアント開発と現実的なUniRx使いこなし術【Unite 2017 Tokyo】「黒騎士と白の魔王」にみるC#で統一したサーバー/クライアント開発と現実的なUniRx使いこなし術
【Unite 2017 Tokyo】「黒騎士と白の魔王」にみるC#で統一したサーバー/クライアント開発と現実的なUniRx使いこなし術
 
凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public
 
MagicOnion~C#でゲームサーバを開発しよう~
MagicOnion~C#でゲームサーバを開発しよう~MagicOnion~C#でゲームサーバを開発しよう~
MagicOnion~C#でゲームサーバを開発しよう~
 
(C#,멀티쓰레드강좌)쓰레드, STA, MTA개요, 간단한 멀티쓰레드 예제_닷넷,C#,WPF,자마린실무강좌
(C#,멀티쓰레드강좌)쓰레드, STA, MTA개요, 간단한 멀티쓰레드 예제_닷넷,C#,WPF,자마린실무강좌(C#,멀티쓰레드강좌)쓰레드, STA, MTA개요, 간단한 멀티쓰레드 예제_닷넷,C#,WPF,자마린실무강좌
(C#,멀티쓰레드강좌)쓰레드, STA, MTA개요, 간단한 멀티쓰레드 예제_닷넷,C#,WPF,자마린실무강좌
 

Viewers also liked

What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
Yoshifumi Kawai
 
HttpClient詳解、或いは非同期の落とし穴について
HttpClient詳解、或いは非同期の落とし穴についてHttpClient詳解、或いは非同期の落とし穴について
HttpClient詳解、或いは非同期の落とし穴について
Yoshifumi Kawai
 
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
Yoshifumi Kawai
 

Viewers also liked (12)

Pythonによる非同期プログラミング入門
Pythonによる非同期プログラミング入門Pythonによる非同期プログラミング入門
Pythonによる非同期プログラミング入門
 
async/await不要論
async/await不要論async/await不要論
async/await不要論
 
Modern C# Programming 現代的なC#の書き方、ライブラリの選び方
Modern C# Programming 現代的なC#の書き方、ライブラリの選び方Modern C# Programming 現代的なC#の書き方、ライブラリの選び方
Modern C# Programming 現代的なC#の書き方、ライブラリの選び方
 
Overview of the .Net Collection Framework and Immutable Collections
Overview of the .Net Collection Framework and Immutable CollectionsOverview of the .Net Collection Framework and Immutable Collections
Overview of the .Net Collection Framework and Immutable Collections
 
ZeroFormatter/MagicOnion - Fastest C# Serializer/gRPC based C# RPC
ZeroFormatter/MagicOnion - Fastest C# Serializer/gRPC based C# RPCZeroFormatter/MagicOnion - Fastest C# Serializer/gRPC based C# RPC
ZeroFormatter/MagicOnion - Fastest C# Serializer/gRPC based C# RPC
 
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
 
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
Metaprogramming Universe in C# - 実例に見るILからRoslynまでの活用例
 
HttpClient詳解、或いは非同期の落とし穴について
HttpClient詳解、或いは非同期の落とし穴についてHttpClient詳解、或いは非同期の落とし穴について
HttpClient詳解、或いは非同期の落とし穴について
 
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
 
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
「黒騎士と白の魔王」gRPCによるHTTP/2 - API, Streamingの実践
 
RuntimeUnitTestToolkit for Unity
RuntimeUnitTestToolkit for UnityRuntimeUnitTestToolkit for Unity
RuntimeUnitTestToolkit for Unity
 
NextGen Server/Client Architecture - gRPC + Unity + C#
NextGen Server/Client Architecture - gRPC + Unity + C#NextGen Server/Client Architecture - gRPC + Unity + C#
NextGen Server/Client Architecture - gRPC + Unity + C#
 

Similar to An other world awaits you

Javaセキュアコーディングセミナー東京第2回演習の解説
Javaセキュアコーディングセミナー東京第2回演習の解説Javaセキュアコーディングセミナー東京第2回演習の解説
Javaセキュアコーディングセミナー東京第2回演習の解説
JPCERT Coordination Center
 
Javaセキュアコーディングセミナー東京第2回演習
Javaセキュアコーディングセミナー東京第2回演習Javaセキュアコーディングセミナー東京第2回演習
Javaセキュアコーディングセミナー東京第2回演習
JPCERT Coordination Center
 
Tokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテストTokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテスト
Yohei Sato
 
Java puzzlers 2013 at JavaFesta Japan
Java puzzlers 2013 at JavaFesta JapanJava puzzlers 2013 at JavaFesta Japan
Java puzzlers 2013 at JavaFesta Japan
Yoshio Terada
 
分散メモリ環境におけるシェルスクリプトの高速化手法の提案
分散メモリ環境におけるシェルスクリプトの高速化手法の提案分散メモリ環境におけるシェルスクリプトの高速化手法の提案
分散メモリ環境におけるシェルスクリプトの高速化手法の提案
Keisuke Umeno
 
関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会
Koichi Sakata
 
ループその2
ループその2ループその2
ループその2
TENTO_slide
 

Similar to An other world awaits you (20)

Keep yourself up to date
Keep yourself up to dateKeep yourself up to date
Keep yourself up to date
 
Javaセキュアコーディングセミナー東京第2回演習の解説
Javaセキュアコーディングセミナー東京第2回演習の解説Javaセキュアコーディングセミナー東京第2回演習の解説
Javaセキュアコーディングセミナー東京第2回演習の解説
 
Unit test in android
Unit test in androidUnit test in android
Unit test in android
 
Javaセキュアコーディングセミナー東京第2回演習
Javaセキュアコーディングセミナー東京第2回演習Javaセキュアコーディングセミナー東京第2回演習
Javaセキュアコーディングセミナー東京第2回演習
 
[xDNCL] 配布資料
[xDNCL] 配布資料[xDNCL] 配布資料
[xDNCL] 配布資料
 
trueコマンドに0以外の終了コードをはかせる方法
trueコマンドに0以外の終了コードをはかせる方法trueコマンドに0以外の終了コードをはかせる方法
trueコマンドに0以外の終了コードをはかせる方法
 
Ajax 応用
Ajax 応用Ajax 応用
Ajax 応用
 
Tokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテストTokyor14 - R言語でユニットテスト
Tokyor14 - R言語でユニットテスト
 
20141129-dotNet2015
20141129-dotNet201520141129-dotNet2015
20141129-dotNet2015
 
jenkinsで遊ぶ
jenkinsで遊ぶjenkinsで遊ぶ
jenkinsで遊ぶ
 
Java初心者勉強会(2015/08/07)資料
Java初心者勉強会(2015/08/07)資料Java初心者勉強会(2015/08/07)資料
Java初心者勉強会(2015/08/07)資料
 
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_cccJEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
 
Java puzzlers 2013 at JavaFesta Japan
Java puzzlers 2013 at JavaFesta JapanJava puzzlers 2013 at JavaFesta Japan
Java puzzlers 2013 at JavaFesta Japan
 
Async design with Unity3D
Async design with Unity3DAsync design with Unity3D
Async design with Unity3D
 
PHP language update 201211
PHP language update 201211PHP language update 201211
PHP language update 201211
 
分散メモリ環境におけるシェルスクリプトの高速化手法の提案
分散メモリ環境におけるシェルスクリプトの高速化手法の提案分散メモリ環境におけるシェルスクリプトの高速化手法の提案
分散メモリ環境におけるシェルスクリプトの高速化手法の提案
 
関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会関ジャバ JavaOne Tokyo 2012報告会
関ジャバ JavaOne Tokyo 2012報告会
 
ループその2
ループその2ループその2
ループその2
 
タダで始めるテストファースト入門 ~ C# Express + NUnit
タダで始めるテストファースト入門 ~ C# Express + NUnitタダで始めるテストファースト入門 ~ C# Express + NUnit
タダで始めるテストファースト入門 ~ C# Express + NUnit
 
Junit4
Junit4Junit4
Junit4
 

More from 信之 岩永

Code Contracts in .NET 4
Code Contracts in .NET 4Code Contracts in .NET 4
Code Contracts in .NET 4
信之 岩永
 

More from 信之 岩永 (20)

YouTube ライブ配信するようになった話
YouTube ライブ配信するようになった話YouTube ライブ配信するようになった話
YouTube ライブ配信するようになった話
 
C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0
 
C# コンパイラーの書き換え作業の話
C# コンパイラーの書き換え作業の話C# コンパイラーの書き換え作業の話
C# コンパイラーの書き換え作業の話
 
Unicode文字列処理
Unicode文字列処理Unicode文字列処理
Unicode文字列処理
 
C# 8.0 非同期ストリーム
C# 8.0 非同期ストリームC# 8.0 非同期ストリーム
C# 8.0 非同期ストリーム
 
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)
 
.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#
 
C# 7.2 with .NET Core 2.1
C# 7.2 with .NET Core 2.1C# 7.2 with .NET Core 2.1
C# 7.2 with .NET Core 2.1
 
C#言語機能の作り方
C#言語機能の作り方C#言語機能の作り方
C#言語機能の作り方
 
Unityで使える C# 6.0~と .NET 4.6
Unityで使える C# 6.0~と .NET 4.6Unityで使える C# 6.0~と .NET 4.6
Unityで使える C# 6.0~と .NET 4.6
 
それっぽく、適当に
それっぽく、適当にそれっぽく、適当に
それっぽく、適当に
 
Modern .NET
Modern .NETModern .NET
Modern .NET
 
.NET Compiler Platform
.NET Compiler Platform.NET Compiler Platform
.NET Compiler Platform
 
Deep Dive C# 6.0
Deep Dive C# 6.0Deep Dive C# 6.0
Deep Dive C# 6.0
 
Code Contracts in .NET 4
Code Contracts in .NET 4Code Contracts in .NET 4
Code Contracts in .NET 4
 
今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略
 
今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略
 
C# design note sep 2014
C# design note sep 2014C# design note sep 2014
C# design note sep 2014
 
.NET vNext
.NET vNext.NET vNext
.NET vNext
 
Coding Interview
Coding InterviewCoding Interview
Coding Interview
 

An other world awaits you

  • 1. An other world awaits you 避けては通れない非同期処理
  • 2. A n o t h e r w o r l d a w a i t s y o u 別世界があなたを待っています C# 5.0 C# 4.0 • Async C# 3.0 • Dynamic • LINQ C# 2.0 これ • Generics C# 1.0 asynchrony • Managed 非同期 ※VB 7~11の歴史でもある
  • 3. A n o t h e r w o r l d a w a i t s y o u 別世界があなたを待っています  こんな世界  フリーズしない世界  バッテリーの持ちがいい世界 C# 5.0 • Async  “今までも、俺ならできたよ”  訓練された人しかできない  訓練された人でも超大変 見合ったコストでは“できない”
  • 6. C# 5.0  非同期処理 最大の売り  async/await 本日の主役  Caller Info  CallerFilePath/CallerLineNumber/CallerMemberName属性  細かい仕様変更/バグ修正  foreach変数  オーバーロード解決  名前付き引数の評価順序 http://ufcpp.net/study/csharp/ap_ver5.html http://msdn.microsoft.com/en-us/library/hh678682.aspx
  • 7. C# 5.0のasync/await  Taskクラス  async修飾子  await演算子 async Task<string> GetAsync(string url) { var client = new HttpClient(); var res = await client.GetAsync(url); var content = await res.Content.ReadAsStringAsync(); return content; } 同期処理の場合と ほぼ同じフローで非同期処理
  • 8. await t.Wait(); • wait for t: 類義語はstay(とどまる) • スレッドを止めて待つ await t; • await t: 類義語はextpect(期待する) • スレッドを止めずにコールバックを待つ 重要: ちゃんと非同期
  • 9. もう少し複雑な例  複数の確認ダイアログ表示 確認フロー ゲームで 確認 1 Yes レア アイテムですよ? アイテムを チェック 結果表示 合成します No 確認 2 Yes 合成強化済みですよ? チェック No Yes 確認 3 もう強化限界ですよ? チェック No
  • 10. 同期 if (this.Check1.IsChecked ?? false) { var result = Dialog.ShowDialog("確認 1", "1つ目の確認作業"); if (!result) return false; } if (this.Check2.IsChecked ?? false) { var result = Dialog.ShowDialog("確認 2", "2つ目の確認作業"); if (!result) return false; } if (this.Check3.IsChecked ?? false) { var result = Dialog.ShowDialog("確認 3", "3つ目の確認作業"); if (!result) return false; } return true;
  • 11. 非同期(旧) if (this.Check1.IsChecked ?? false)  画面に収まるように { Dialog.BeginShowDialog("確認 1", "1つ目の確認作業", result => { if (!result) { onComplete(false); フォント サイズ調整 return; } if (this.Check2.IsChecked ?? false) { Dialog.BeginShowDialog("確認 2", "2つ目の確認作業", result2 => {  4ptです if (!result2) { onComplete(false); return; } if (this.Check3.IsChecked ?? false) {  ほんの84行ほど Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 => { onComplete(result3); }); } else onComplete(true); });  ちなみに } else if (this.Check3.IsChecked ?? false) { Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 => { onComplete(result3); }); }  部分部分を関数化して多少は else onComplete(true); }); } else if (this.Check2.IsChecked ?? false) 整理できます { Dialog.BeginShowDialog("確認 2", "2つ目の確認作業", result => { if (!result) { onComplete(false); return;  ダイアログ3つだからまだこの } if (this.Check3.IsChecked ?? false) { Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 => 程度で済んでます { onComplete(result); }); } else onComplete(true); }); } else if (this.Check3.IsChecked ?? false) { Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 => { onComplete(result3); }); } else onComplete(true);
  • 12. 非同期(C# 5.0) if (this.Check1.IsChecked ?? false) { var result = await Dialog.ShowDialogAsync("確認 1", "1つ目の確認作業"); if (!result) return false; } if (this.Check2.IsChecked ?? false) { var result = await Dialog.ShowDialogAsync("確認 2", "2つ目の確認作業"); if (!result) return false; } if (this.Check3.IsChecked ?? false) { var result = await Dialog.ShowDialogAsync("確認 3", "3つ目の確認作業"); if (!result) return false; } • await演算子が増えただけ return true; • ダイアログが増えても平気
  • 13. さて、今日の議題 こんなに便利なasync/awaitですが…  中身はどうなってるの?  非同期処理の実行インフラ  awaitの実現方法  awaitで万事解決?  そうでもない  並列処理  イベント型の非同期  データフロー
  • 15. 非同期インフラ  スレッド  スレッド プール  I/O完了ポート  UIスレッドと同期コンテキスト
  • 16. 非同期インフラ  スレッド  スレッド プール  I/O完了ポート  UIスレッドと同期コンテキスト
  • 17. スレッド  非同期処理の最も低レイヤーな部分 Worker1 Worker2 static void Main() { var t = new Thread(Worker2); CPUをシェア t.Start(); 定期的に切り替え Worker1(); }  スレッドはプリエンプティブ※なマルチタスク  ハードウェア タイマーを使って強制割り込み 高負荷  OSが特権的にスレッド切り替えを行う ※preemptive: 先売権のある
  • 18. スレッド  非同期処理の最も低レイヤーな部分  ただし、高負荷 for (int i = 0; i < 1000; i++) { var t = new Thread(Worker); t.Start(); } 1000個の処理を同時実行  細々とした大量の処理をこなすには向かない  切り替え(コンテキスト スイッチ)のコストが高すぎる
  • 19. スレッドを立てるコスト  スレッドに紐づいたデータ  カーネル ステート: 1kBくらい  ローカル スタック: 1MBくらい  イベント発生  Thread Attached/Detachedイベント
  • 20. スレッド切り替えコスト  カーネル モードに移行  レジスターの値を保存  lockを獲得  次に実行するスレッドの決定 どれも結構時間が  lockを解放 かかる処理  スレッドの状態を入れ替え  レジスターの値の復元  カーネル モードから復帰 ※ http://blogs.msdn.com/b/larryosterman/archive/2005/01/05/347314.aspx
  • 21. スレッド切り替えコスト削減  切り替えの原因  一定時間経過  待機ハンドル待ち(lock獲得とか)  同期I/O待ち  対策  そもそもスレッドを立てない 自前実装大変  lock-freeアルゴリズム利用 スレッドは直接使わない  I/Oは非同期待ち スレッド プール I/O完了ポート
  • 22. スレッド切り替えコスト削減  切り替えの原因  一定時間経過 .NETでいうと  待機ハンドル待ち(lock獲得とか) • Threadクラスは使わない  同期I/O待ち .NET for Windowsストア アプリではついに削除された • Taskクラスを使う  対策 3.5以前の場合はThraedPoolクラスやTimerクラス、 .NET IAsyncResultインターフェイス  そもそもスレッドを立てない 自前実装大変  lock-freeアルゴリズム利用 スレッドは直接使わない  I/Oは非同期待ち スレッド プール I/O完了ポート
  • 23. 非同期インフラ  スレッド  スレッド プール  I/O完了ポート  UIスレッドと同期コンテキスト
  • 24. 2種類のマルチタスク プリエンプティブ • ハードウェア タイマーを使って強制割り込み • OSが特権的にスレッド切り替えを行う • 利点: 公平(どんなタスクも等しくOSに制御奪われる) • 欠点: 高負荷(切り替えコストと使用リソース量) 協調的※ • 各タスクが責任を持って終了する • 1つのタスクが終わるまで次のタスクは始まらない • 利点: 低負荷 • 欠点: 不公平(1タスクの裏切りが、全体をフリーズさせる) ※cooperative
  • 25. スレッド プール  スレッドを可能な限り使いまわす仕組み  プリエンプティブなスレッド数本の上に  協調的なタスク キューを用意 スレッド プール キュー 数本のスレッド 新規タスク だけ用意 タスク1 タスク2 タスクは一度 キューに溜める … 空いているスレッドを探して実行 (長時間空かない時だけ新規スレッド作成)
  • 26. スレッド プールの性能的な工夫  Work Stealing Queue  lock-free実装なローカル キュー  できる限りスレッド切り替えが起きないように グローバル ローカル ローカル キュー キュー1 キュー2 スレッド1 スレッド2 ① スレッドごとに ② キューを持つ ローカル キュー が空のとき、 他のスレッドから タスクを奪取
  • 27. 参考: lock-freeアルゴリズム lockベース OS機能に頼った競合回避 lock (_sync) (カーネル モード移行あり) { _value = SomeOperation(_value); } lock-free(interlockedベース) 競合してたらやり直す long oldValue1, oldValue2; CPUのInterlocked命令※を利用 do { 競合頻度が低い時に高効率 oldValue1 = _value; var newValue = SomeOperation(_value); oldValue2 = Interlocked.CompareExchange( ref _value, newValue, oldValue1); } while (oldValue1 != oldValue2); ※ アトミック性を保証したCPU命令。カーネル モード移行と比べるとだいぶ低不可 interlocked: 連結した、連動した
  • 28. 非同期インフラ  スレッド  スレッド プール  I/O完了ポート  UIスレッドと同期コンテキスト
  • 29. おさらい  待機しちゃダメ  × lock 何もしていないのにスレッド立てっぱなし  × 同期I/O待ち • スタック(1MB)取りっぱなし • スレッド切り替えの誘発  × Thread.Sleep • スレッド プール上でも新しいスレッド が立ってしまう  ○ 非同期I/O I/O完了ポート  ○ タイマー(Task.Delay)
  • 30. I/O  Input/Output  CPUと、CPUの外との入出力 待機しちゃダメ  CPU内での計算と比べると、数ケタ遅い ユーザー入力 通信 CPU ストレージ ハードウェア タイマー
  • 31. I/O完了ポート※  I/Oを待たない  コールバックを登録  I/O完了後、スレッド プールで続きの処理を行う アプリ あるスレッド スレッド プール タスク1 コールバック登録後、 コールバック タスク2 登録 すぐにスレッド上での … 処理を終了 I/O完了ポート I/O開始 I/O完了 (OSカーネル内) ハードウェア ※ I/O completion port
  • 32. 非同期APIを使いましょう  同じ非同期処理でも Task.Run(() => req.GetResponse()); • スレッド内で同期I/O • スレッド立てっぱなし req.GetResponseAsync(); • I/O完了ポートを使って非同期I/O • コールバック登録後、スレッドを解放
  • 33. Sleepもダメ(例: Sleep Sort)  値に比例してSleepすればソートできるんじゃね?  というネタ。 ×スレッド立ててSleep new Thread(_ => { Thread.Sleep(t); q.Enqueue(x); }).Start(); 要素数分のスレッドが立つ 要素数×1MBのスタック確保 既定の設定だと、スレッド1,000個くらいで Out of Memory ○タイマー利用+コールバック Task.Delay(t).ContinueWith(_ => q.Enqueue(x));
  • 34. ダメなものは最初から提供しない  Windows 8世代のAPI 50ミリ秒以上かかる可能性のあるAPIは 非同期APIのみ提供  WinRT  ファイル操作、ネットワーク、グラフィック  ランチャー、ダイアログ表示  .NET 4.5で追加されたクラス  HttpClientなど  .NET for Windows ストア アプリ  同期I/O APIやThreadクラス削除
  • 35. 非同期インフラ  スレッド  スレッド プール  I/O完了ポート  UIスレッドと同期コンテキスト
  • 36. シングル スレッド必須  スレッド安全なコードは高コスト  いっそ、単一スレッド動作を前提に  典型例はGUI  C#/.NETに限らずたいていのGUIフレームワークはシングル スレッド動作  低レイヤーAPI(DirectXとかOpenGLとか)も、1つのス レッドからしか扱えない
  • 37. 典型例: UIスレッド  GUIは単一スレッド動作(UIスレッド)  ユーザーからの入力受け付け  画面の更新 UIスレッド 他のスレッド ユーザー グラフィック からの入力 更新 OK 処理中は 他のスレッドから 応答不可 は更新不可
  • 38. 矛盾 シングル スレッド推奨 単一スレッドからしか UI更新できない OK そのスレッドを止める とUIフリーズ マルチ スレッド推奨
  • 39. 解決策 1. スレッド プールで重たい処理 2. UIスレッドに処理を戻してからUI更新 UIスレッド 他のスレッド Task.Run 重たい処理 Dispatcher.Invoke 更新 OK 戻す役割を担うのが ディスパッチャー※ ※ dispatcher: 配送者
  • 40. ディスパッチャーの利用例  WPFの場合※ Task.Run(() => { var result = HeavyWork(); this.Dispatcher.Invoke(() => { this.List.ItemsSource = result; }); }); • あまり意識したくないんだけども • 自動的にはやってくれないの? ※ WPF、Silverlight、WinRT XAMLでそれぞれ書き方が少しずつ違う
  • 41. 自動化するにあたって  処理の実行場所には文脈がある  同期コンテキスト※と呼ぶ 文脈 要件 スレッド プール どのスレッドで実行しててもいい 実行効率最優先 GUI UIの更新は、UIスレッド上での実行 が必要(ディスパッチャーを経由) Web API どのスレッドで実行してもいい ただし、どのWebリクエストに対す る処理か、紐づけが必要  適切な同期コンテキストを拾って実行する必要がある ※ synchronization context もちろん自作も可能
  • 42. 同期コンテキストの利用例 var context = SynchronizationContext.Current; Task.Run(() => { var result = HeavyWork(); context.Post(r => { this.List.ItemsSource = (IEnumerable<int>)r; }, result); }); 文脈に応じた適切な実行方法をしてくれる • WPFの場合はディスパッチャーへのPost  あとは、ライブラリの中で自動的に同期コンテキ ストを拾ってくれれば万事解決…?
  • 43. 完全自動化無理でした  スレッド プール中で同期コンテキストを拾うと、 スレッド プールにしか返ってこない  BackgroundWorkerのDoWork内で、別の BackgroundWorkerを作ると破綻  必ずUIスレッドに処理を戻すと実行効率悪い  ぎりぎりまでスレッド プール上で実行したい  ネイティブ⇔.NETをまたげない  WinRT XAML UIだと自動化無理 つまるところ、JavaScriptとかでそれができているのは • 単一UIフレームワーク • 実行効率度外視
  • 44. なので、ある程度の自由を  TaskSchdulerの選択 既定動作(スレッド プール) Task.Run(() => HeavyWork()) .ContinueWith(t => { // スレッド プール上で実行される }); 同期コンテキストを拾う Task.Run(() => HeavyWork()) .ContinueWith(t => { // UI スレッド上で実行される }, TaskScheduler.FromCurrentSynchronizationContext());
  • 45. ちなみに、awaitは  既定で同期コンテキストを拾う 既定動作(同期コンテキストを拾う) var result = await Task.Run(() => HeavyWork()); 挙動の変更(同期コンテキストを拾わない) var result = await Task.Run(() => HeavyWork()) .ConfigureAwait(false);  awaitした場所で同期コンテキストを拾う  (ライブラリ内でなく)利用側で  なので、ネイティブAPIを使っていても、ちゃんと拾える
  • 47. awaitが解決するもの(1)  1往復の、pull型非同期処理 例: 前述のGUIでの非同期処理 UIスレッド 他のスレッド Task.Run 重たい処理 await var result = await Task.Run(() => HeavyWork());
  • 48. awaitが解決するもの(2)  1往復の、pull型非同期処理 非同期I/Oも好例 UIスレッド I/O完了ポート Task.Run I/O開始 await I/O完了 var result = await client.GetAsync();
  • 49. awaitが解決するもの(3)  1往復の、pull型非同期処理 ダイアログ ウィンドウの表示なんかも UIスレッド GUIフレームワーク ダイアログ表示 ユーザーがダイアログを操作 OK await var result = await dialog.ShowAsync(); 「UIスレッドの入れ子」みたいなことができないので、 一度処理を抜けなきゃいけない
  • 50. awaitが解決しないもの  並列処理  イベント型非同期  そもそも制御フローを書きにくいもの
  • 51. 並列処理  マルチコアCPUを使いきりたい  Parallelクラス Parallel.ForEach(data, x => { // 並列に実行したい処理 });  Parallel LINQ var results = data.AsParallel() .Select(x => /* 並列に実行したい処理 */);
  • 52. イベント型非同期処理  複数件、push型 処理側 発生側  センサーAPI ハンドラー登録  サーバーからのpush通知 イベント発生  ユーザー操作への応答も ある意味では非同期処理 イベント発生 イベント発生 void Init() { var sensor = Accelerometer.GetDefault(); イベント発生 sensor.Shaken += sensor_Shaken; } void sensor_Shaken( Accelerometer sender, AccelerometerShakenEventArgs args) { // イベント処理 }
  • 53. WinRTのイベント型非同期処理  ネイティブの向こう側で起こっていること  同期コンテキストを自動的に拾えない void Init() { var sensor = Accelerometer.GetDefault(); sensor.Shaken += sensor_Shaken; } void sensor_Shaken( Accelerometer sender, AccelerometerShakenEventArgs args) { // イベント処理 UIを更新するなら明示的な } ディスパッチャー利用必須
  • 54. WinRTのイベント型非同期処理  Rx※とか使うのがいいかも var sensor = Accelerometer.GetDefault(); Observable.FromEventPattern(sensor, "Shaken") .ObserveOn(SynchronizationContext.Current) .Subscribe(args => { // イベント処理 TaskSchedulerと同じ感覚 }); ※ Reactive Extensions http://msdn.microsoft.com/en-us/data/gg577609 NuGet取得可・Windowsストア アプリでの利用可
  • 56. 制御フローを書きにくいもの  WFとか  http://msdn.microsoft.com/en-us/vstudio/aa663328  TPL Dataflowとか  http://msdn.microsoft.com/en-us/devlabs/gg585582.aspx
  • 58. 非同期処理 おさらい  待機しちゃダメ task.ContinueWith( コールバック);  コールバック  中断と再開 やるべきことは イテレーターと同じ  同期コンテキスト
  • 59. イテレーター  中断と再開 class MethodEnumerator : IEnumerator<int> { public int Current { get; private set; } private int _state = 0; public bool MoveNext() { switch (_state) { case 0: IEnumerable<int> Method() { Current = 1; _state = 1; yield return 1; return true; case 1: Current = 2; yield return 2; _state = 2; return true; case 2: default: } return false; } } }
  • 60. イテレーター  中断と再開 class MethodEnumerator : IEnumerator<int> { public int Current { get; private set; } private int _state = 0; public bool MoveNext() { switch (_state) { case 0: 状態の記録 IEnumerable<int> Method() Current = 1; { Current = 1; yield return 1; _state = 1; _state = 1; return true; return 1: case true; 中断 case 1: = 2; Current _state = 2; return true; 再開用のラベル yield return 2; case 2: default: } return false; } } }
  • 61. awaitの展開結果(コンセプト)  コンセプト的には イテレーター + ContinueWith 状態の記録 _state = 1; if (!task1.IsCompleted) async Task<int> Method() { { task1.ContinueWith(a); var x = await task1; var y = await task2; return; 中断 } } case 1: 再開用のラベル var x = task1.Result; 結果の受け取り
  • 62. awaitの展開結果  実際はもう少し複雑  Awaiterというものを介していたり(Awaitableパターン) _state = 1; var awaiter1 = task1.GetAwaiter(); if (!awaiter1.IsCompleted) { awaiter1.OnCompleted(a); • こいつが同期コンテキスト return; を拾い上げていたりする } • Awaiterを自作することで、 case 1: awaitの挙動を変更可能 var x = awaiter1.GetResult(); • Task以外もawait可能
  • 63. 逆に  C# 5.0が使えなくても  イテレーターを使ってawaitに似た非同期処理可能 そこそこめんどくさい  C#が使えなくても  IEnumerableを自作する程度の手間でawaitに似た(略 絶望的にめんどくさい けど、性能は出ると思う C#使わせてください。ほんとお願いします。
  • 65. まとめ  スレッド(Thread)は直接使わない  スレッド プール(Task)を使う  await  wait(stay)と違って、await(expect)  イテレーター(中断と再開) + ContinueWith(コールバック)  同期コンテキスト  awaitにだって…できないことは…ある  並列処理、イベント型非同期  そもそも同期で書きにくいもの