Upload
yoshifumi-kawai
View
30.300
Download
1
Embed Size (px)
DESCRIPTION
RIA アーキテクチャー研究会 第3回 http://atnd.org/events/24951
Citation preview
C#次世代非同期処理概観
Task vs Reactive Extensions
2012/3/10 #riaarch
Yoshifumi Kawai @neuecc
Name => Yoshifumi Kawai
仕事は近頃はASP.NETで、あまりRIAじゃなかったり
まあ、HTML5もRIAですし!
Twitter => @neuecc
HN => neuecc
読むときは“のいえ”と読ませてます
サイトのドメイン(特に意味はない)を繋いだだけで、識別子になればそれだけでいいと思って発音考えてなかったので割とアレ
Microsoft MVP for Visual C#(2011/4-)
Profile
http://linqjs.codeplex.com/
LINQのJavaScript移植
linq.js
http://reactiveproperty.codeplex.com/
Rxスタイルのバインディング補助ライブラリ
.NET4/SL5/SL5/WP7.1 対応
ReactiveProperty
非同期処理とは何か
非同期処理の苦痛
.NET Frameworkにおける非同期処理パターン
C# 5.0(Visual Studio 11)の非同期処理(async/await)
Task vs Rx
Agenda
What is asynchronous?
Parallel?
並列 - 一つの処理をバラして同時実行して高速化
Parallel.For/Parallel.ForEach/Parallel LINQ
Concurrent?
並行 - 個別な処理が相互作用したりしなかったり
Thread, Task
Asynchronous!
非同期 - ブロックしない処理
UIスレッドを止めなかったりIO処理効率化だったり
DownloadStringAsyncなどやBeginXxx-EndXxxなど
ThreadやTaskを使っても勿論いい
非同期って?
「クラウド」並のバズワード、とか言ってみたり
RIA的にはUIスレッドのブロックいくない
ユーザーエクスペリエンス的には当然だろ常考
Silverlight, Windows Phone, Windows 8(WinRT)には最初から非同期メソッドしか用意されていない(同期処理不可!)
JavaScriptなんて最初からそれOnlyですしね
Web的には同時接続対策
C10K問題
http://www.hyuki.com/yukiwiki/wiki.cgi?TheC10kProblem
node.jsの流行にC#erとしてはC# 5.0で対抗する!
非同期はなぜ重要か
Demo
Asynchronous Blues
単発ならそれほどでもない
ネストすると人類の手に負えない
ネスト宇宙ヤバイ
var wc = new WebClient(); wc.DownloadStringCompleted += (_, res) => { var wc2 = new WebClient(); wc2.DownloadStringCompleted += (__, res2) => { MessageBox.Show(res2.Result); }; wc2.DownloadStringAsync(new Uri(res.Result)); }; wc.DownloadStringAsync(new Uri("http://localhost:8403/TestAPI.ashx"));
というかもう無理。
例外処理不能
var wc = new WebClient(); wc.DownloadStringCompleted += (_, res) => { if (res.Error != null) { MessageBox.Show(res.Error.Message); } var wc2 = new WebClient(); wc2.DownloadStringCompleted += (__, res2) => { if (res2.Error != null) { MessageBox.Show(res2.Error.Message); } MessageBox.Show(res2.Result); }; wc2.DownloadStringAsync(new Uri(res.Result)); }; wc.DownloadStringAsync(new Uri("http://localhost:8403/TestAPI.ashx"));
Patterns of Async Operation
Asynchronous Programming Model
BeginXxx-EndXxxのペアによる非同期処理
WebRequest#BeginGetResponse-EndGetResponseのようなもの
とにかく面倒くさい
ラムダ式登場で割と緩和されたけれど
正しく使うのは慣れが必要
後述するTaskやRxのベースとして活躍
.NETにおけるもっともプリミティブな非同期システムと考えていただければ
APMとは
var req = WebRequest.Create("http://localhost:8403/TestAPI.ashx"); req.BeginGetResponse(ar => { try { var res = req.EndGetResponse(ar); var req2 = WebRequest.Create(new StreamReader(res.GetResponseStream()).ReadToEnd()); req2.BeginGetResponse(ar2 => { try { var res2 = req2.EndGetResponse(ar2); var result = new StreamReader(res2.GetResponseStream()).ReadToEnd(); Dispatcher.BeginInvoke(() => MessageBox.Show(result)); } catch (Exception ex2) { Dispatcher.BeginIncoke(()=> MessageBox.Show("inner error " + ex2.Message)); } }, null); } catch (Exception ex1) { Dispatcher.BeginIncoke(()=> MessageBox.Show("outer error " + ex1.Message)); } }, null);
catchできるのは同じ関数のブロック内だけ
UIスレッドへ通達する必要がある
非同期処理中の例外はEndのタイミングで伝送
Event-Based Asynchronous Pattern
XxxAsyncメソッド + XxxCompletedイベントのペアによる非同期処理
WebClient#DownloadStringAsyncのようなもの
素の状態のC#では割と楽な書き方
リクエストの発行が後になるという順序の問題を抱える
後述するTaskやRxの登場でサヨウナラ行き
TaskやRxでラップするのも面倒くさいという
EAPとは
var wc = new WebClient(); wc.DownloadStringCompleted += (_, res) => { if (res.Error != null) { MessageBox.Show(res.Error.Message); } var wc2 = new WebClient(); wc2.DownloadStringCompleted += (__, res2) => { if (res2.Error != null) { MessageBox.Show(res2.Error.Message); } MessageBox.Show(res2.Result); }; wc2.DownloadStringAsync(new Uri(res.Result)); }; wc.DownloadStringAsync(new Uri("http://localhost:8403/TestAPI.ashx"));
先にイベントを登録して
リクエストの発行は後で行う(順序が逆だとダメ!)
UIスレッドまわりは自動で面倒みてくれる(WebClientの場合)
Task Based Asynchronous Pattern
.NET 4/SL5以降登場したTPLを使った非同期処理
TPLはTask Parallel Libraryの略
Threadを概ね置き換えます
Task.Factory.FromAsyncでAPMをラップ
.NET 4.5やWinRTにはフレームワークに最初からラップ済み or TAP用に書かれた専用メソッドが大量追加
WebRequest#GetResponseAsyncとか
WebClient#DownloadStringAsyncとか
TAPとは
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); var req = WebRequest.Create("http://localhost:8403/TestAPI.ashx"); Task.Factory.FromAsync<WebResponse>(req.BeginGetResponse, req.EndGetResponse, null) .ContinueWith(res => { var req2 = WebRequest.Create(new StreamReader(res.Result.GetResponseStream()).ReadToEnd()); return Task.Factory.FromAsync<WebResponse>(req2.BeginGetResponse, req2.EndGetResponse, null); }) .Unwrap() .ContinueWith(res => { MessageBox.Show(new StreamReader(res.Result.GetResponseStream()).ReadToEnd()); }, uiScheduler) .ContinueWith(res => { MessageBox.Show(res.Exception.Message); }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, uiScheduler);
UIスレッド用スケジューラを用意することで個別BeginInvoke不要
ネストしたTaskは明示的にUnwrap
APMをラップする
例外は最後に統一的処理が可能 階層構造はフラット
Reactive Extensions
http://msdn.microsoft.com/en-us/devlabs/gg577609
LINQベースのイベント・非同期処理ライブラリ
LINQ to Events, LINQ to Asynchronous
WP7には同梱、JavaScript版もあり
@ITで連載やってるので読んでね!
http://www.atmarkit.co.jp/fdotnet/introrx/index/index.
html
LINQベースのため合成処理が異常なほど強力
非同期処理だけではなくあらゆる用途に使える
Rxとは
var req = WebRequest.Create("http://localhost:8403/TestAPI.ashx"); Observable.FromAsyncPattern<WebResponse>(req.BeginGetResponse, req.EndGetResponse)() .SelectMany(res => { var next = new StreamReader(res.GetResponseStream()).ReadToEnd(); var req2 = WebRequest.Create(next); return Observable.FromAsyncPattern<WebResponse>(req2.BeginGetResponse, req2.EndGetResponse)(); }) .ObserveOnDispatcher() .Subscribe( res => MessageBox.Show(new StreamReader(res.GetResponseStream()).ReadToEnd()), ex => MessageBox.Show(ex.Message));
APMをラップする
階層構造はフラット 例外は最後に統一的処理が可能
実行スレッドのコントロール
事前にラップ用拡張メソッドを用意すると
非同期処理とは思えないほど超絶的に本体がシンプルになる
// Taskもそうですが、ラップ用メソッドは事前に拡張メソッドを作っておくと良いです
public static class WebRequestExtensions { public static IObservable<WebResponse> GetResponseAsObservable(this WebRequest request) { return Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse)(); } public static IObservable<string> DownloadStringAsObservable(this WebRequest request) { return request.GetResponseAsObservable() .Select(res => { using (var sr = new StreamReader(res.GetResponseStream())) { return sr.ReadToEnd(); } }); } }
WebRequest.Create("http://localhost:8403/TestAPI.ashx") .DownloadStringAsObservable() .SelectMany(x => WebRequest.Create(x).DownloadStringAsObservable()) .ObserveOnDispatcher() .Subscribe( x => MessageBox.Show(x), ex => MessageBox.Show(ex.Message));
Future
Visual Studio 11
http://www.microsoft.com/visualstudio/11/ja-jp
async/await構文でContinueWithが言語サポート
それに加えて標準クラスライブラリにもラップ済みor専用処理のXxxAsync/XxxTaskAsyncメソッドが大量追加
ライブラリはWinRT/.NET 4.5のみでSL5やWP7には現在はなし
async/await自体は可能なので、自分でラップを作って拡張メソッドとして用意すればいい
C# 5.0
private async void Button_Click_1(object sender, RoutedEventArgs e) { try { var nextUrl = await new WebClient().DownloadStringTaskAsync("http://localhost:8403/TestAPI.ashx"); var result = await new WebClient().DownloadStringTaskAsync(nextUrl); MessageBox.Show(result); } catch (Exception ex) { MessageBox.Show(ex.Message); } }
asyncキーワードを足す
awaitキーワードで継続 awaitキーワードで継続
同期構文と何も変わらない例外処理
Task vs RxだったらRxのほうが使いやすい
ただしC# 5.0ではTaskが圧倒的に使いやすい
じゃあ将来を見据えてTaskで作ったほうが……?
Rx(IObservable<T>)もawaitさせることはできます
現在は実験版リリース or ver.2 Betaのみ対応
Rx is awaitable
var nextUrl = await WebRequest.Create("http://localhost:8403/TestAPI.ashx").DownloadStringAsObservable() var result await WebRequest.Create(nextUrl).DownloadStringAsObservable()
TaskのメソッドはToObservableでRxに変換可能
SelectManyなど一部の主要なRxの合成系メソッドはTaskを直接渡すことに対応
まだ実験版, Rx v2 Betaのみ
Task is Observable
new WebClient().DownloadStringTaskAsync("url").ToObservable() .SelectMany(x => new WebClient().DownloadStringTaskAsync(x)) .Subscribe()
Rxはawait可能であること、C# 5.0でTaskベースの非同期処理がライブラリレベルでも主流になることから、Rx v2ではObservable.FromAsyncPatternはObsolete(非推奨)となりました
Task.FromAsyncを使い、必要ならばToObservable
していく
FromAsyncPattern
Silverlight 4ならばRx一択
Taskないですから
将来VS11/SL5に移行してTask中心になったとしても、そのままawait出来るので問題はない
Silverlight 5ではVS11を見据えて基本的なラップはTaskのFromAsyncで行う
処理自体はContinueWithで組んでもいいけれど、素のTaskは使いづらいのでRxを使うのがベター
同じく将来的にはRxはawait可能なので大丈夫
WinRTや.NET 4.5 WPFではasync/awaitで
一つの値を返すものはTask+async/awaitがベスト
どれを選択すべきか
Reactive Extensions
単体の非同期処理はTaskに譲っても、Rxにはイベント処理+それらから流れてくるストリームをそのまま非同期処理して、非同期ストリーム化させる、などなど、非同期においても、まだ多くの役割がある
TPL Dataflow
.NET 4.5搭載のTaskベースの非同期ストリーム処理ライブラリ
Rxと直接対決になるかどうかはまだ分からない
非同期ストリーム処理
ダウンロードセンター or NuGetのpre releaseから入手可能
http://www.microsoft.com/download/en/details.aspx?i
d=29058
.NET 4.5/SL5/WP 7.1専用
高速化
.NET Portable Library対応
一つのプロジェクトで全てのプラットフォームに対応可能になる
Rx v2 Beta
C# 5.0超素晴らしい
Rxの立ち位置は、非同期処理では若干難しくなってきている
将来的にはシンプルなケースでは完全不要でしょう
ではRxに価値はないかというとそんなことはなくて、メインであるイベント処理への活用や非同期ストリーム処理などの方向で使えます
なにより、現時点での唯一の解
まとめ