33
C#次世代非同期処理概観 Task vs Reactive Extensions 2012/3/10 #riaarch Yoshifumi Kawai @neuecc

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

Embed Size (px)

DESCRIPTION

RIA アーキテクチャー研究会 第3回 http://atnd.org/events/24951

Citation preview

Page 1: C#次世代非同期処理概観 - Task vs Reactive Extensions

C#次世代非同期処理概観

Task vs Reactive Extensions

2012/3/10 #riaarch

Yoshifumi Kawai @neuecc

Page 2: C#次世代非同期処理概観 - Task vs Reactive Extensions

Name => Yoshifumi Kawai

仕事は近頃はASP.NETで、あまりRIAじゃなかったり

まあ、HTML5もRIAですし!

Twitter => @neuecc

HN => neuecc

読むときは“のいえ”と読ませてます

サイトのドメイン(特に意味はない)を繋いだだけで、識別子になればそれだけでいいと思って発音考えてなかったので割とアレ

Microsoft MVP for Visual C#(2011/4-)

Profile

Page 3: C#次世代非同期処理概観 - Task vs Reactive Extensions

http://neue.cc/

C#とかLINQ

配色がアレ

サイト

Page 4: C#次世代非同期処理概観 - Task vs Reactive Extensions

http://linqjs.codeplex.com/

LINQのJavaScript移植

linq.js

Page 5: C#次世代非同期処理概観 - Task vs Reactive Extensions

http://reactiveproperty.codeplex.com/

Rxスタイルのバインディング補助ライブラリ

.NET4/SL5/SL5/WP7.1 対応

ReactiveProperty

Page 6: C#次世代非同期処理概観 - Task vs Reactive Extensions

非同期処理とは何か

非同期処理の苦痛

.NET Frameworkにおける非同期処理パターン

C# 5.0(Visual Studio 11)の非同期処理(async/await)

Task vs Rx

Agenda

Page 7: C#次世代非同期処理概観 - Task vs Reactive Extensions

What is asynchronous?

Page 8: C#次世代非同期処理概観 - Task vs Reactive Extensions

Parallel?

並列 - 一つの処理をバラして同時実行して高速化

Parallel.For/Parallel.ForEach/Parallel LINQ

Concurrent?

並行 - 個別な処理が相互作用したりしなかったり

Thread, Task

Asynchronous!

非同期 - ブロックしない処理

UIスレッドを止めなかったりIO処理効率化だったり

DownloadStringAsyncなどやBeginXxx-EndXxxなど

ThreadやTaskを使っても勿論いい

非同期って?

Page 9: C#次世代非同期処理概観 - Task vs Reactive Extensions

「クラウド」並のバズワード、とか言ってみたり

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で対抗する!

非同期はなぜ重要か

Page 10: C#次世代非同期処理概観 - Task vs Reactive Extensions

Demo

Page 11: C#次世代非同期処理概観 - Task vs Reactive Extensions

Asynchronous Blues

Page 12: C#次世代非同期処理概観 - Task vs Reactive Extensions

単発ならそれほどでもない

ネストすると人類の手に負えない

ネスト宇宙ヤバイ

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"));

Page 13: C#次世代非同期処理概観 - Task vs Reactive Extensions

というかもう無理。

例外処理不能

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"));

Page 14: C#次世代非同期処理概観 - Task vs Reactive Extensions

Patterns of Async Operation

Page 15: C#次世代非同期処理概観 - Task vs Reactive Extensions

Asynchronous Programming Model

BeginXxx-EndXxxのペアによる非同期処理

WebRequest#BeginGetResponse-EndGetResponseのようなもの

とにかく面倒くさい

ラムダ式登場で割と緩和されたけれど

正しく使うのは慣れが必要

後述するTaskやRxのベースとして活躍

.NETにおけるもっともプリミティブな非同期システムと考えていただければ

APMとは

Page 16: C#次世代非同期処理概観 - Task vs Reactive Extensions

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のタイミングで伝送

Page 17: C#次世代非同期処理概観 - Task vs Reactive Extensions

Event-Based Asynchronous Pattern

XxxAsyncメソッド + XxxCompletedイベントのペアによる非同期処理

WebClient#DownloadStringAsyncのようなもの

素の状態のC#では割と楽な書き方

リクエストの発行が後になるという順序の問題を抱える

後述するTaskやRxの登場でサヨウナラ行き

TaskやRxでラップするのも面倒くさいという

EAPとは

Page 18: C#次世代非同期処理概観 - Task vs Reactive Extensions

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の場合)

Page 19: C#次世代非同期処理概観 - Task vs Reactive Extensions

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とは

Page 20: C#次世代非同期処理概観 - Task vs Reactive Extensions

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をラップする

例外は最後に統一的処理が可能 階層構造はフラット

Page 21: C#次世代非同期処理概観 - Task vs Reactive Extensions

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とは

Page 22: C#次世代非同期処理概観 - Task vs Reactive Extensions

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をラップする

階層構造はフラット 例外は最後に統一的処理が可能

実行スレッドのコントロール

Page 23: C#次世代非同期処理概観 - Task vs Reactive Extensions

事前にラップ用拡張メソッドを用意すると

非同期処理とは思えないほど超絶的に本体がシンプルになる

// 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));

Page 24: C#次世代非同期処理概観 - Task vs Reactive Extensions

Future

Page 25: C#次世代非同期処理概観 - Task vs Reactive Extensions

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

Page 26: C#次世代非同期処理概観 - Task vs Reactive Extensions

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キーワードで継続

同期構文と何も変わらない例外処理

Page 27: C#次世代非同期処理概観 - Task vs Reactive Extensions

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()

Page 28: C#次世代非同期処理概観 - Task vs Reactive Extensions

TaskのメソッドはToObservableでRxに変換可能

SelectManyなど一部の主要なRxの合成系メソッドはTaskを直接渡すことに対応

まだ実験版, Rx v2 Betaのみ

Task is Observable

new WebClient().DownloadStringTaskAsync("url").ToObservable() .SelectMany(x => new WebClient().DownloadStringTaskAsync(x)) .Subscribe()

Page 29: C#次世代非同期処理概観 - Task vs Reactive Extensions

Rxはawait可能であること、C# 5.0でTaskベースの非同期処理がライブラリレベルでも主流になることから、Rx v2ではObservable.FromAsyncPatternはObsolete(非推奨)となりました

Task.FromAsyncを使い、必要ならばToObservable

していく

FromAsyncPattern

Page 30: C#次世代非同期処理概観 - Task vs Reactive Extensions

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がベスト

どれを選択すべきか

Page 31: C#次世代非同期処理概観 - Task vs Reactive Extensions

Reactive Extensions

単体の非同期処理はTaskに譲っても、Rxにはイベント処理+それらから流れてくるストリームをそのまま非同期処理して、非同期ストリーム化させる、などなど、非同期においても、まだ多くの役割がある

TPL Dataflow

.NET 4.5搭載のTaskベースの非同期ストリーム処理ライブラリ

Rxと直接対決になるかどうかはまだ分からない

非同期ストリーム処理

Page 32: C#次世代非同期処理概観 - Task vs Reactive Extensions

ダウンロードセンター 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

Page 33: C#次世代非同期処理概観 - Task vs Reactive Extensions

C# 5.0超素晴らしい

Rxの立ち位置は、非同期処理では若干難しくなってきている

将来的にはシンプルなケースでは完全不要でしょう

ではRxに価値はないかというとそんなことはなくて、メインであるイベント処理への活用や非同期ストリーム処理などの方向で使えます

なにより、現時点での唯一の解

まとめ