Upgrade to Pro — share decks privately, control downloads, hide ads and more …

次世代非同期処理概観 Task vs Reactive Extensions

次世代非同期処理概観 Task vs Reactive Extensions

Avatar for Yoshifumi Kawai

Yoshifumi Kawai

March 10, 2012
Tweet

More Decks by Yoshifumi Kawai

Other Decks in Technology

Transcript

  1.  Name => Yoshifumi Kawai ⚫ 仕事は近頃はASP.NETで、あまりRIAじゃなかったり ⚫ まあ、HTML5もRIAですし! 

    Twitter => @neuecc  HN => neuecc ⚫ 読むときは“のいえ”と読ませてます ⚫ サイトのドメイン(特に意味はない)を繋いだだけ で、識別子になればそれだけでいいと思って発音考 えてなかったので割とアレ ⚫ Microsoft MVP for Visual C#(2011/4-) Profile
  2.  Parallel? ⚫ 並列 - 一つの処理をバラして同時実行して高速化 ⚫ Parallel.For/Parallel.ForEach/Parallel LINQ 

    Concurrent? ⚫ 並行 - 個別な処理が相互作用したりしなかったり ⚫ Thread, Task  Asynchronous! ⚫ 非同期 - ブロックしない処理 ⚫ UIスレッドを止めなかったりIO処理効率化だったり ⚫ DownloadStringAsyncなどやBeginXxx-EndXxxなど ⚫ ThreadやTaskを使っても勿論いい 非同期って?
  3.  「クラウド」並のバズワード、とか言ってみたり  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で対抗する! 非同期はなぜ重要か
  4.  単発ならそれほどでもない  ネストすると人類の手に負えない ネスト宇宙ヤバイ 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"));
  5.  というかもう無理。 例外処理不能 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"));
  6.  Asynchronous Programming Model  BeginXxx-EndXxxのペアによる非同期処理  WebRequest#BeginGetResponse-EndGetResponse のようなもの 

    とにかく面倒くさい ⚫ ラムダ式登場で割と緩和されたけれど  正しく使うのは慣れが必要  後述するTaskやRxのベースとして活躍  .NETにおけるもっともプロミティブな非同期シス テムと考えていただければ APMとは
  7. 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のタイミングで伝送
  8.  Event-Based Asynchronous Pattern  XxxAsyncメソッド + XxxCompletedイベントのペア による非同期処理 

    WebClient#DownloadStringAsyncのようなもの  素の状態のC#では割と楽な書き方  リクエストの発行が後になるという順序の問題を 抱える  後述するTaskやRxの登場でサヨウナラ行き  TaskやRxでラップするのも面倒くさいという EAPとは
  9. 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の場合)
  10.  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とは
  11. 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をラップする 例外は最後に統一的処理が可能 階層構造はフラット
  12.  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とは
  13. 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をラップする 階層構造はフラット 例外は最後に統一的処理が可能 実行スレッドのコントロール
  14. 事前にラップ用拡張メソッドを用意すると 非同期処理とは思えないほど超 絶的に本体がシンプルになる // 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));
  15.  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
  16. 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キーワードで継続 同期構文と何も変わらない例外処理
  17.  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()
  18.  TaskのメソッドはToObservableでRxに変換可能  SelectManyなど一部の主要なRxの合成系メソッド はTaskを直接渡すことに対応 ⚫ まだ実験版, Rx v2 Betaのみ

    Task is Observable new WebClient().DownloadStringTaskAsync("url").ToObservable() .SelectMany(x => new WebClient().DownloadStringTaskAsync(x)) .Subscribe()
  19.  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がベスト どれを選択すべきか
  20.  ダウンロードセンター 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