Share
Embed
Reactive Extensions v 2.0 (+ Silverlight 5)
Yoshifumi Kawai
March 31, 2012
Transcript
37 slides
-
Twitter => @neuecc HN => neuecc ⚫ 読むときは“のいえ”と読ませてます
⚫ サイトのドメイン(特に意味はない)を繋いだだけ で、識別子になればそれだけでいいと思って発音考 えてなかったので割とアレ ⚫ Microsoft MVP for Visual C#(2011/4-) ⚫ 明日、期限切れor更新の勧告が! ⚫ というわけで若干そわそわ中 Profile -
History Rx in DevLabs (2009/11/18) Rx v1.0 (2011/1/21) Rx v2.0
Beta (2011/1/21) RxJS 2.0 Beta (2011/12/30) Phone.Reactive RxJS v1.0 (2010/3/18) Rx Experimental in BCL??? (Future) -
準標準、的なライブラリ Microsoftが開発、配布しているものだけど標準ラ イブラリではない 将来的に標準入りするかはまだ分からない .NET3.5,4/SL3,4,5/WP7/WinRT/JavaScript 対応
⚫ Portable Library対応 ⚫ ※ただしRx v2.0は.NET4.5/SL5/WinRT/WP7.1のみ 位置付け -
実際使うならRx v1.0 Stableが安定 Rx v1.0 ExperimentalはRx v2.0 Betaに吸収された感
SilverightではRx v2.0 BetaはSL5のみ利用可 種類大杉 -
Silverlight 5からTPLが搭載されました! ⚫ 非同期とかパラレルとか面倒見てくれるライブラリ でもVS11ではC# 5.0 async/awaitが使えません ⚫
async/awaitはTask利用なので使えてもいいのにぃー ⚫ まあ、SL5のTaskと.NET 4.5のTaskは少し違うから…… SilverlightでC# 5.0で非同期抹殺でHappyはまだ先 Task Parallel Library -
単発ならそれほどでもない ネストすると人類の手に負えない ネスト宇宙ヤバイ 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")); -
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.EndGetRespo } 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)); -
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.as 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").DownloadStringAsObservabl 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() -
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に価値はないかというとそんなことはなくて、
メインであるイベント処理への活用や非同期スト リーム処理などの方向で使えます ⚫ なにより、現時点での唯一の解 まとめ