非同期なんて、もう何も怖くない 「もっさり」なんて言わせない ++C++; (@ufcpp) 岩永 信之 発表内容 どうして非同期が必要なの? 非同期処理はどうやって書くの? 非同期処理を意識させたら負けかなと 思っている どうして非同期処理が必要? 非同期処理の必要性 もっさり [副](スル) 1. やぼったいさま。あかぬけしていないさま。 「~ した人」 2. ぼんやりしていて気のきかないさま。 「~ つっ立っている」 もっさりの原因 イベント ハンドラー内で重たい処理 void Button_Click( object sender, RoutedEventArgs e) { 重たい処理(); } 結果がこれだよ デスクトップ アプリ 応答していません Silverlight 非同期処理 UIスレッドをブロックしちゃダメ 同期実行 非同期実行 UIスレッド 応答不能 重たい 処理 UIスレッド 応答可能 別スレッド 重たい 処理 ユーザーからの入力イベント I/O完了待ち CPUの計算速度と比べて、周辺機器のア クセスは数ケタ低速 CPU処理 ハードウェア処理 数千命令 実行可能 待ってたらもったいない 当然、非同期処理が必要 I/O完了待ち(通信) 特に、通信は秒単位で待たされることも CPU処理 秒単位 秒単位ってのがどのくらいやばいかというと・・・ 0.5秒固まったら「使いにくい」 3秒固まったら「バグだ」 10秒固まったら「パソコンが壊れた」 と言われる。 ということで SilverlightのI/Oは必ず非同期 チェック外せない ※ WCFサービス参照画面 「非同期操作の生成」しかできない 非同期処理はどうやって書くの?同期処理とどう変わるの? 同期 VS 非同期 例 サーバーからデータを取得 ボタンを押して データを表示 同期処理 UIスレッド : Page Button Click この間、 フリーズ : DataClient new DataClient() ① client.GetData()② ダウンロード 完了待ち ItemsSource = data③ 同期処理のコード 同期的に書くならコードはシンプル ① var client = new DataClient(); ② var data = client.GetData(); ③ grid.ItemsSource = data; 順序通り 非同期処理 UIスレッド 別スレッド : Page : DataClient Button Click いったん 処理終了 new DataClient() ① client.GetDataAsync() ② 完了通知 ItemsSource = data③ ダウンロード 完了待ち 非同期処理のコード EAP†(後述)の場合 var client = new DataClient(); ① client.GetDataCompleted += (sender, e) => 階層深くなる Dispatcher.Invoke(() => { ③ grid.ItemsSource = e.Result; }; ② client.GetDataAsync(); 順序ばらばら †Event-based Asynchronous Pattern 非同期処理はどうやって書くの?パターンは1通り? 非同期処理のバリエーション パターン1: APM Asynchronous Programming Model – Begin/Endのペア – Beginにコールバックを渡す IAsyncResult Begin…(Callback) TResult End…(IAsyncResult) .NET Frameworkにはこのパターンが多い (1.0時代からあるクラスは全部) APM利用例 var client = new DataClient(); client.BeginGetData(ar => Dispatcher.Invoke(() => { var data = client.EndGetData(ar); grid.ItemsSource = data; }; 匿名メソッドがなかった頃は このEnd…の呼び出しが面倒だった 非同期処理の基本形 でも、未経験者は中々なじめないらしい パターン2: EAP Event-based Asynchronous Pattern – Asyncメソッド/Completedイベントのペア – 処理完了時にイベントが発生 void …Async() event Handler<TResult> …Completed Silverlightにはこのパターンが多い (.NET 2.0時代ごろから増えてきた) EAP利用例 var client = new DataClient(); client.GetDataCompleted += (sender, e) => Dispatcher.Invoke(() => { grid.ItemsSource = e.Result; }; client.GetDataAsync(); 順序ばらばら eventはVisual Studioの補助受けやすい – 「+=」入力後、[tab]キー でも記述順狂うし、実行効率も悪い パターン3: TAP Task Asynchronous Pattern – Task<T>を返すAsyncメソッド Task<TResult> …Async() .NET 4以降の本命 Silverlightには次期バージョンで入りそう ※Task的なものを自作するのは結構大変なので注意 ※代わりに使えそうなのはReactive Extensions (Rx) TAP利用例 var client = new DataClient(); client.GetDataAsync() 継続呼び出し .ContinueWith(t => { grid.ItemsSource = t.Result; }, scheduler); このパターンの真価はC# 5.0で… TAP利用例(C# 5.0(予定)) var client = new DataClient(); var data = await client.GetDataAsync(); grid.ItemsSource = t.Result; コンパイラーが継続 呼び出しに変換する 同期呼び出しとほとんど同じ var client = new DataClient(); var data = client.GetData(); grid.ItemsSource = t.Result; 差はawaitの有無だけ 参考: http://msdn.microsoft.com/vstudio/async 非同期処理を意識させたら負けかなと思っている 非同期処理のカプセル化 (ライブラリに隠ぺい) ライブラリ化 非同期処理、 利用パターンはある程度決まっている クラス書いてみた パターン1: 非同期コマンド対 開始/中止のコマンド対 開始 中止 <Button Content="開始" Command="{Binding Do.StartCommand}" /> <Button Content="中止" Command="{Binding Do.CancelCommand}" /> 非同期コマンド対 クラス public class AsyncCommandPair { public AsyncCommandPair(action) { … } public ICommand StartCommand { get; } public ICommand CancelCommand { get; } public bool IsBusy { get; } } 非同期に処理したい内容をコンストラクターで渡す StartCommand: 非同期処理を開始 CancelCommand: 実行中の非同期処理を中止 IsBusy: 非同期処理実行中ならtrue 非同期コマンド対 利用例 _Do = new AsyncCommandPair<object, int>( async (p, progress, cancel) => { var data = await client.GetDataAsync( progress, cancel); grid.ItemsSource = data; }); AsyncCommandPair<object, int> _Do; public IAsyncCommandPair Do { get { return _Do; } } パターン2: 非同期キャッシュ 非同期に遅延ロード&キャッシュ 大分類 小分類 食品 米 飲料 麺類 キッチン用品 肉・卵 野菜 あまり更新 されないデータ 調味料 <ListBox ItemsSource="{Binding Categories.Value}" /> 非同期キャッシュ クラス public class AsyncCache<T> { public AsyncCache(Func<Task<T>> loader) { … } public T Value { get; } public ICommand InvalidateCommand { get; } } ロード用のメソッドをコンストラクターで渡す Value: 初回読み出し時に非同期ロード開始 ロード完了時にPropertyChangedイベント発生 2回目以降はキャッシュを返す InvalidateCommand: キャッシュ無効化 非同期キャッシュ 利用例 _Data = new AsyncCache<IEnumerable<Data>>( client.GetDataAsync ); AsyncCache<IEnumerable<Data>> _Data; public AsyncCache<IEnumerable<Data>> Data { get { return _Data; } } まとめ まとめ GUI、特にSilverlightでは非同期処理必須 非同期処理は面倒だけども… – C# 5.0で大分楽になる予定 – ある程度決まったパターンがあるのでライブ ラリ化 参考: http://msdn.microsoft.com/vstudio/async
© Copyright 2024 ExpyDoc