Your SlideShare is downloading. ×
Reactive Extensionsで非同期処理を簡単に
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Reactive Extensionsで非同期処理を簡単に

16,162
views

Published on

すまべん関東 Rx資料

すまべん関東 Rx資料

Published in: Technology

0 Comments
21 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
16,162
On Slideshare
0
From Embeds
0
Number of Embeds
9
Actions
Shares
0
Downloads
74
Comments
0
Likes
21
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Reactive ExtensionsでWP7の非同期処理を簡単に @neuecc – 2011/5/21
  • 2. Profile Twitter => @neuecc Blog => http://neue.cc/ HNはneuecc 読むときは“のいえ”と読ませてます  ドメイン繋いだだけなので発音するの考えてなかっ た(のでccは抜きで←発音しにくいですから) Microsoft MVP for Visual C#(2011/4-) WP7で作った物  ReactiveOAuth, Utakotoha WP7の好きなtheme  light + lime
  • 3. Agenda LINQの概要/LINQとしてのReactive Extensions 非同期処理の面倒さと如何にRxが癒すか .NETにおける非同期パターンの説明 Rxの基本(購読, キャンセル, 例外処理) 非同期処理で使うRxのメソッド概略 作った物紹介
  • 4. Linq to Introduction
  • 5. Language INtegrated Query// クエリ構文var query = from x in source where x % 2 == 0 select x * x;// メソッド構文var query = source .Where(x => x % 2 == 0) .Select(x => x * x);
  • 6. LINQって何? データソースを統一的な書法で処理できる WhereでフィルタしてSelectで射影できるならそれ はLINQって言えます! SQL関係ないし、C#も飛び越えて生きる概念 JavaScript移植もあるしね  linq.js – http://linqjs.codeplex.com/  RxJS(Reactive Extensions for JavaScript)
  • 7. LINQのデータソースとはto Objects to Xml to Sql 配列 XML Database List<T> (JSON) Stream 無限リストto Events to Asynchronous TextChanged IO – WebRequest ジェスチャー Timer – ポーリング センサー Thread – 長時間かかる処理 MusicPlayer Reactive Extensions
  • 8. Reactive Extensions = Linq to Events Linq to AsynchronousLINQにおけるデータソースの拡張……というだけじゃない時間という軸を中心にした基盤
  • 9. Rxの基本軸は時間IE<T> length IE<T> async eventIO<T> time=> IE<T>も乗っかることで「全てのデータソース」が合成可能に!
  • 10. Async Programming Blues
  • 11. 古き良き同期コードvar req = WebRequest.Create("http://hoge/");var res = req.GetResponse();var str = new StreamReader(res.GetResponseStream()).ReadToEnd(); 簡単。でも、Silverlight/WP7には同期APIは無い。  UIがブロックされるのダメ絶対 Thread立ててそっちで実行させれば?  まあそうです  でもないものはないのでしょうがない  そのかわり特に気を使わなくても必ずUIノンブロッ キングになる(※但しCPUヘヴィな処理は除く)
  • 12. しょうがないので非同期で書くvar req = WebRequest.Create("http://hoge");req.BeginGetResponse(ar =>{ var res = req.EndGetResponse(ar); var str = new StreamReader(res.GetResponseStream()) .ReadToEnd(); Dispatcher.BeginInvoke(() => MessageBox.Show(str));}, null); -> EndHoge 非同期はBeginHoge 基本、クロージャ全開で書く 面倒くさいけれど、まあこれぐらいなら?
  • 13. ネストするとかなりヤバいvar req = WebRequest.Create("http://hoge");req.BeginGetResponse(ar =>{ var res = req.EndGetResponse(ar); var url = new StreamReader(res.GetResponseStream()) .ReadToEnd(); var req2 = WebRequest.Create(url); req2.BeginGetResponse(ar2 => { var res2 = req2.EndGetResponse(ar2); var str = new StreamReader(res2.GetResponseStream()) .ReadToEnd(); Dispatcher.BeginInvoke(() => MessageBox.Show(str)); }, null);}, null);
  • 14. 通信箇所に例外処理は必須 WP7ではネットワーク周りのコードでは100%例外 発生の可能性がある 圏外だったり通信が超低速だったりすると?  Hello, Timeout.  山崎春のWebException祭り  何の手立てもしないとアプリ落ちるよ 予期される例外だし、固有の後処理もあるだろう し、復帰可能にすべきなので、その場その場で catchして始末するのが無難
  • 15. 内側なのは見た目だけvar req = WebRequest.Create("http://hoge");req.BeginGetResponse(ar =>{ 非同期中に起こる例外はEnd時に戻される try { var res = req.EndGetResponse(ar); var url = new StreamReader(res.GetResponseStream()).ReadToEnd(); var req2 = WebRequest.Create(url); req2.BeginGetResponse(ar2 => ここの例外をcatchしてない { var res2 = req2.EndGetResponse(ar2); var str = new StreamReader(res2.GetResponseStream()).ReadToEnd(); Dispatcher.BeginInvoke(() => textBlock1.Text = str); }, null); } catchできるのは同じ関数のブロック内だけ catch(WebException e) { Dispatcher.BeginInvoke(() => MessageBox.Show(e.ToString())); }}, null);
  • 16. もはやカオスすぎて頭痛いvar req = WebRequest.Create("http://hoge");req.BeginGetResponse(ar =>{ try { var res = req.EndGetResponse(ar); var url = new StreamReader(res.GetResponseStream()).ReadToEnd(); var req2 = WebRequest.Create(url); req2.BeginGetResponse(ar2 => { try { var res2 = req2.EndGetResponse(ar2); var str = new StreamReader(res2.GetResponseStream()).ReadToEnd(); Dispatcher.BeginInvoke(() => MessageBox.Show(str)); } catch (WebException e) { Dispatcher.BeginInvoke(() => MessageBox.Show(e.ToString())); } }, null); } catch (WebException e) { Dispatcher.BeginInvoke(() => MessageBox.Show(e.ToString())); }}, null);
  • 17. Reactive Extensionsを使うとネストが消滅し完全フラット 拡張メソッド(後で説明します) WebRequest.Create("http://hoge") .GetResponseAsObservable() .Select(res => new StreamReader(res.GetResponseStream()).ReadToEnd()) .SelectMany(s => WebRequest.Create(s).GetResponseAsObservable()) .Select(res => new StreamReader(res.GetResponseStream()).ReadToEnd()) .ObserveOnDispatcher() .Subscribe( s => MessageBox.Show(s), e => MessageBox.Show(e.ToString())); 内部で発生する例外は全てここで扱える
  • 18. Rxを使うことの利点 ネストがなくなって平らに 統一的な例外処理が可能+ その他の機能もいっぱい  リトライ処理  イベントやシーケンスとの合成など
  • 19. Asynchronous Patterns
  • 20. 非同期のもと 非同期パターンは概ね二つ  APM(Asynchronous Programming Model)  BeginXxx-EndXxx  WebRequest.BeginGetResponseとか  EAP(Event-based Asynchronous Pattern)  XxxAsync-XxxCompleted  WebClient.DownloadStringAsync/Copletedとか 将来的には?  Rx(WP7では標準搭載ですが.NET4ではまだ)  Task(.NET4では標準搭載ですがWP7ではまだ)  C# 5.0 Async(まだCTP, 恐らく2年ぐらい先)
  • 21. どっちがいいの? Rxで使うならAPMのほうが相性良い APMは上から下まで流れてるが、EAPは最後に発火 させなければならない これはネストする場合に致命的に面倒// APMWebRequest.Create("http://hoge") .GetResponseAsObservable() .Subscribe();// EAP Subscribe後に発火var wc = new WebClient();wc.DownloadStringCompletedAsObservable() .Subscribe();wc.DownloadStringAsync("http://hoge");
  • 22. 拡張メソッドのすゝめ APM,WebRequest->WebResponseはプリミティブ すぎて、一々Stream扱ったり面倒くさい WebClientのDownloadString的なのが欲しい なら拡張メソッドで自作すれば解決 FromEventやFromAsyncPatternは定型句なので、こ ちらも拡張メソッドで隔離するのがお薦め
  • 23. FromEvent(FromEventPattern) 戻り値はIEvent<EventArgs>のIO<T>public staticIObservable<IEvent<DownloadStringCompletedEventArgs>> DownloadStringCompletedAsObservable(this WebClient webClient){ return Observable.FromEvent< DownloadStringCompletedEventHandler, DownloadStringCompletedEventArgs>( h => h.Invoke, // おまじない h => webClient.DownloadStringCompleted += h, h => webClient.DownloadStringCompleted -= h);} FromEvent<EventHandler,EventArgs>
  • 24. FromAsyncPattern FromAsyncPatternの戻り値はデリゲート  つまり自分でInvokeするまで実行されない 拡張メソッドにするなら即実行のほうが便利? Task.Factory.StartNew的なイメージでpublic static IObservable<WebResponse> GetResponseAsObservable(this WebRequest request){ return Observable.FromAsyncPattern<WebResponse>( request.BeginGetResponse, request.EndGetResponse) .Invoke();}
  • 25. DownloadStringAsync(の自作) Stringが戻ったほうが便利ですよね POSTなども同じように作っておくと楽になるpublic static IObservable<string> DownloadStringAsync(this WebRequest request){ return request.GetResponseAsObservable() .Select(res => { using (var stream = res.GetResponseStream()) using (var sr = new StreamReader(stream)) { return sr.ReadToEnd(); } });}
  • 26. 最初の例もこんなにスッキリ Rxが提供してくれているのは基本的な道具 Rxは「分離しやすい」のも特徴的なメリット <T>への拡張メソッドや、IO<T>->IO<T>の拡張メ ソッドなどを作って、すっきりさせようWebRequest.Create("http://hoge") .DownloadStringAsync() .SelectMany(s => WebRequest.Create(s).DownloadStringAsync()) .ObserveOnDispatcher() .Subscribe( s => MessageBox.Show(s), e => MessageBox.Show(e.ToString()));
  • 27. Basics of Rx
  • 28. 書き方の基本 System.Observableを参照する Microsoft.Phone.Reactiveを参照する  WP7には標準搭載  NET4版は別途インストールしてSystem.Reactiveを 定形的な流れは FromEvent/FromAsyncして(IO<T>の生成) SelectなりWhereなりLINQでデータを加工して ObserveOnDispatcherして(値をUIスレッドに戻す) Subscribeする
  • 29. ところでキャンセルしたい// Subscribeの戻り値はIDisposableなので、Disposeすればおkvar disposable = AsyncMethod().Subscribe();disposable.Dispose();// イベントの場合はデタッチになるよvar buttonClick = button.ClickAsObservable().Subscribe();buttonClick.Dispose();// Listに入れてまとめてDisposeとかvar disposables = new List<IDisposable>();disposable.Add(button.ClickAsObservable().Subscribe());disposable.Add(button.ClickAsObservable().Subscribe());disposable.ForEach(d => d.Dispose());// IList<IDisposable>はCompositeDisposableというのもあるvar disposables = new CompositeDisposable();disposable.Dispose();
  • 30. 例外処理は? 何も書かない  例外はcatchせずスローされてくる SubscribeのonErrorに書く  Rxのチェーンで発生した例外を全てcatchする  ここに空のものを書けば例外無視が成立とかも  ※Tips:一部メソッドが間に挟まれていると(Publishなど) 場合によってはcatchされなくなることも(Publishは内部 で自前でSubscribeしているためチェーンが途切れてる) Catchメソッドを使う  例外の型を指定した通常のtry-catchに近いもの  戻り値にEmptyを返せば終了、何か別の値を返せばそ れを代替として流すということになる
  • 31. 基本的なのはこの3つ// 何も書かないので例外がスローされてくるWebRequest.Create("http://hoge") .DownloadStringAsync() .Subscribe(Debug.WriteLine);// SubscribeのonErrorで全てcatchWebRequest.Create("http://hoge") .DownloadStringAsync() .Subscribe(Debug.WriteLine, e => { });// CatchでWebExceptionだけcatchWebRequest.Create("http://hoge") .DownloadStringAsync() .Catch((WebException e) => Observable.Empty<string>()) .Subscribe(Debug.WriteLine);
  • 32. Compose Patterns
  • 33. SelectMany - 直列の結合 AsyncA AsyncBZip - 並列の結合 AsyncA Result AsyncB
  • 34. SelectMany + Zip - 合成の例 AsyncA AsyncB Result AsyncC AsyncA().SelectMany(a => AsyncB(a)) .Zip(AsyncC(), (b, c) => new { b, c });
  • 35. ForkJoin - 並列同時実行 AsyncA AsyncB Result AsyncC AsyncDObservable.ForkJoin(AsyncA(), AsyncB(), AsyncC(), AsyncD()) .Select(xs => new { a=xs[0], b=xs[1], c=xs[2], d=xs[3] });
  • 36. 多重from(SelectMany) AsyncA AsyncB AsyncC Result var asyncQuery = from a in AsyncA() from b in AsyncB(a) from c in AsyncC(a, b) select new { a, b, c };
  • 37. Conclusion
  • 38. まとめ FromEvent/Asyncは拡張メソッドで隔離 ついでにReadToEndなんかも隔離 とりあえずSubscribeの前にObserveOnDispatcher onErrorとCatchで例外をコントロール 色々な合成メソッドで流れをコントロール もう非同期なんて怖くない! Rxで書くと実は同期で書くよりも柔軟 むしろもう同期でなんて書きたくない!
  • 39. Real World Rx
  • 40. ReactiveOAuth http://reactiveoauth.codeplex.com/ TwitterとかのOAuth認証/通信用ライブラリ 以下のWP7アプリで使われています!  SongTweeter @iseebi  NumberPush @mitsuba_tan  Utakata TextPad @kaorun  HirganaTwit @hatsune_
  • 41. どんな時に使えるの? Twitterクライアントを作るわけではないけれど、 ステータスだけTwitterに投稿したい、などはよく あること(特に最近は何でもTwitterだし)new OAuthClient(ConsumerKey, ConsumerSecret, accessToken){ MethodType = MethodType.Post, Url = "http://api.twitter.com/1/statuses/update.xml", Parameters = { { "status", "ここに投稿する文章" } }}.GetResponseText() // 投稿して、戻り値を得る.Select(XElement.Parse) // xmlの場合はパースしたいよね.Subscribe(); // なにか処理するなり例外処理入れるなり
  • 42. ポータビリティ WP7だけじゃなく.NET4/SL4向けもあり コードの99%をWP7と共有している  残り1%は最近の更新で.NET版Rxが一部WP7版と互換 なくなったため Rxをベースに置くと、コードの可搬性が圧倒的に 高まる 機能面でもRxにタダ乗り出来るので(エラー・リト ライ・タイムアウトなど全部Rxにおまかせ) ライブラリ本体はシンプルなコードのままで強力 な機能を持てる
  • 43. Utakotoha http://utakotoha.codeplex.com/ 再生中の曲に応じて日本語歌詞を表示する マーケットプレイスで公開中(Free) ソースコードも公開中
  • 44. コードの中身 コードは無理やり全部Rxで割と実験的 GUI周りは強引でボロボロで酷い イベント周りなどは面白く仕上がったかも Modelを別に立てた.NET4クラスライブラリにリン クで参照することによりMSTestでユニットテスト Molesというモックライブラリを使ってWP7のイベ ント自体を乗っ取り(音楽再生の情報をテストのた めに任意に生成したり) 上手くいってるかはノーコメント
  • 45. Deep Dive Rx
  • 46. 公式見るのがいいよやっぱり Data Developer Center - Rx http://msdn.microsoft.com/en-us/data/gg577609 二つのドキュメントが出ています  Hands-on-Lab  チュートリアル式で触りながら分かりやすく  まず見て欲しい  Design Guidelines  マニュアルみたいなもの  更に深くへ
  • 47. 日本語情報は? ねぇよんなもん うちのサイトでも見てください:)  http://neue.cc/  http://neue.cc/category/rx