非同期処理の必要性

非同期なんて、もう何も怖くない
「もっさり」なんて言わせない
++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