Upload
tetsuji-ogata
View
6.105
Download
2
Embed Size (px)
DESCRIPTION
2014/7/12に行われた Mishima.pm#1 で発表したトークのスライドです。
Citation preview
マルチタスクって奥が深いOGATA Tetsuji (@xtetsuji)
2014/07/12 Mishima.pm#1
自己紹介
• 尾形 鉄次 (OGATA Tetsuji)
• Twitter: @xtetsuji
• Blog: http://post.tetsuji.jp/
今回は無事に静岡に来られた!
2013/12/11 Mishima.pm#0
2013年12月に胃潰瘍で入院したときのツイートまとめ - Togetterまとめ http://togetter.com/li/607049
あれから半年
• 半年経過後の胃カメラ検査をつい先日してきました
• 静岡前の胃カメラ、今度は死亡フラグじゃなかった
• 珍しい位置の潰瘍だったけど、なんとか生きてます
• あれから毎月激動で、公私ともに忙しいけれど、皆様の温かいご支援で生きています
今だから復習したい マルチタスク
過去にもマルチタスクの話は 結構しているけど成長が(ry
過去にしているトークの一例
• 「そのsleep、ちょっと待った」(PerlBeginners#13)
• 「イベント駆動とノンブロッキング」(Hokkaido.pm#10)
• 今回はこれらのプラスアルファな感じの話をします
• ビギナー向けにちょうどよい感じ、という言い訳
• みんなで質疑応答をしたりといった感じでよろしくです
色々なPerlのツール• forkや、Parallel::ForkManager、Parallel::Prefork など
• 封印されしithreads (アッ、誰か来た)
• AnyEvent前史のPOEフレームワーク
• AnyEvent、そしてCoroといったMarc Lehmmanツール
• 今昔IO非同期系ツール群
結構雑にカジュアルに
• マルチタスク=複数の仕事をこなす
• 非同期=ノンブロッキング
• 同期=ブロッキング
• 情報系の学術論文でないのであれば、この辺りあんまり厳密に区別しなくてもよいっぽい(頭の中でできることに越した事はありません)
ithreadsが使われない理由• Perlのスレッド(ithreads)は不安定だし効率が悪い
• Perlはグローバルな状態だらけの世界観なので、後付けのスレッドもインタープリタプールを作ったり、色々大変な事をしているので、同情してあげてください
• Windowsにはforkがないので、WindowsのPerlではWin32スレッドを使ったforkのエミューレーションをしているそうです
forkモデル
• fork=フォーク=分岐
• 途中で親と子に分かれてプログラムの分岐を行う
• Perl組み込みのfork関数がそれを実現してくれる
forkサンプル#!/usr/bin/env perl!!use strict;!use warnings;!use utf8;!!binmode STDOUT, ':utf8';!binmode STDERR, ':utf8';!!print "親です。プロセスIDは $$ です。開始します。\n";!!my $pid = fork; # ここで親と子で分岐!!if ( $pid ) {! sleep 2;! print "親です。子のプロセスIDは $pid のようです。しばらく休みます\n";! sleep 10;! print "親です。10秒待ちました。\n";!} elsif ( $pid == 0 ) {! sleep 3;! print "子です。私のプロセスIDは $$ のようです。\n";! for my $i (1..5) {! print "子です。${i}回目。\n";! sleep 1;! }! print "子はしばらく待ちます。先に親が終了してプロンプトに戻ります\n";! sleep 10;!} else {! die "forkできなかった?\n";!}!!print "pid=$pid \$\$=$$\n"; # 親も子も通る
あんまりforkを直接書かない• 見てパッと理解できない
• 多くの子プロセスを作るときにfork()を呼びまくったら結構混乱する
• 親と子でコミュニケーションするにはプロセス間通信が一番プリミティブな方法かな
• 多くのforkが必要になった場合には、これを抽象化したParallel::ForkManagerが役に立つ
Parallel::ForkManagerの例#!/usr/bin/env perl!!use strict;!use warnings;!!use LWP::UserAgent;!use Parallel::ForkManager;!!my @urls = ( map { sprintf "http://example.jp/images/%02d.jpg" } (1..99) );!!my $ua = LWP::UserAgent->new;!!my $pm = Parallel::ForkManager->new(5); # 最大5プロセス並列!!for my $url (@urls) {! my $pid = $pm->start and next;! # 子プロセスの処理! print "プロセスIDが $$ の子プロセスが $url からデータをダウンロードします\n";! ( my $filename = $url ) =~ s{.*/}{};! $ua->get($url, ":content_file" => $filename);! $pm->finish; # 子プロセスを終了!}
並列ダウンロードと マルチタスク
• 外界の影響を受けるダウンロードというタスクの場合、同期的にダウンロードをするとサーバによって待たされるケースがあったりする
• RSSとかを大量にクロールするとか、死活問題
• 例えばParallel::ForkManagerで解決できる
ForkManagerとPrefork
• Parallel::ForkManagerは並列クライアントを作るもの、Parallel::Preforkは並列処理ができるforkモデルのサーバを作るものと覚えておくとよい
• Parallel::ForkManagerは相当以前からDebian/Ubuntuパッケージとしても提供されているので、システムPerlでも使いやすい
• Parallel::Preforkは奥一穂さんによる和製モジュール
forkの問題点と言われること
• プログラムのコピーなのでメモリを食う
• Copy on Write(CoW)というOSの機構により倍々にはならないはずだけど、限界はある
• プロセス間通信が面倒
• これは頑張るか、コストを気にしなければHTTP等のサーバを介したりするのがよいかも
とはいえforkはよく使う
• UNIX/Linuxのサブプロセスの原点fork
• 覚えておくと色々勉強になるし、自分もよく勉強してる
• Perlのsystem関数はforkとexecの組み合わせ
• 先輩プログラマが「フォーク」とか言っているときに、ちょっと知った気になれるの重要
イベント駆動モデル• スレッドともforkとも若干違うマルチタスクモデル
• Apacheのprefork MPMがAjax時代にまつわるC10K問題に対抗できない代替として生まれたともいえるNginxのマルチタスクモデルとして注目された
• Perlでは、AnyEventというイベント駆動フレームワークが以前から主流で、以前からあった同種のイベント駆動モジュールのインターフェースを統一したりもした
AnyEvent
• 以前流行したPOEの後継にあたるものともいえる
• Perlで動作する様々なイベント駆動フレームワークの、今の事実上のデファクトスタンダード
• 裏側のイベント処理実装に様々なものが選べる
• 各種イベント(時間、I/O、シグナル、などなど)で処理を走らせることができる
AnyEventの後ろの実装• AnyEvent::Impl::* 以下にあるものが選べる
• Cocoa、Event、FLTK、IOAsync、POE、Qt、EV、EventLib、Glib、Irssi、Perl (AnyEvent標準の実装)、Tk
• 通常はAnyEvent::Impl::Perlが選ばれるけど、EVを選んだり、場所によって選択を変える場合がある
• Marc Lehmann氏も「POEはやめとけ」と書いている
POEが衰退した理由
• 構造が複雑:OSとほぼ同じ、カーネル・ヒープ構造
• 書き方が複雑
• 静かにプログラムが異常終了していることが多かった
• 重いとも言われていた
daemonを作るwhileループ
• daemon的な常駐プログラムを作る場合、while(1){…} といった無限ループを作ることが定石
• とはいえ、無限ループにsleepをはさみ忘れたりすると、CPU暴走してサーバが死んだりするので怖い
• while無限ループはなるべく自前で書かない方がいいというのが私の持論
AnyEventの方法論
• AnyEvent::Impl::* によっても変わるけど、結局的に、PerlやCのwhile(1){…}が一番根底で走っていると思って差し支えない
• 特にAnyEvent::Impl::Perlの場合は、AnyEvent::Loopがそれらしい
AnyEvent::Loopより
sub run {! one_event while 1;!}
one_event メソッドは多くの仕事をしていますが割愛
AnyEventの活用例
• クライアントでは各種IRCやTwitterボットなどで活用
• サーバではWebサーバ(Twiggyが有名)をはじめとした、任意のTCPサーバが書ける
• これらを組み合わせたりできるので、IRCクライアントでかつWebサーバというプログラムも書ける (Ikachanという良い実装例もある)
AnyEventで時間イベント#!/usr/bin/env perl!!use strict;!use warnings;!use AnyEvent;!!my $cv = AnyEvent->condvar; # 状態変数!my $timer1 = AnyEvent->timer(! after => 1,! cb => sub { print "timer1 hello!\n"; },! interval => 2,!);!my $timer2 = AnyEvent->timer(! after => 1,! cb => sub { print "timer2 hello!\n"; },! interval => 3,!);!my $timer3 = AnyEvent->timer(! after => 1,! cb => sub { print "timer3 hello!\n"; },! interval => 5,!);!my $stop_timer = AnyEvent->timer(! after => 10,! cb => sub { $cv->send("end"); }, # 終わらせる!);!my $val = $cv->recv; # ループをまわす!print "terminate: $val\n";
AnyEventで時間イベント
• 最初はwhileループのほうが良いと思うけど、自分で気をつけてsleep書かなくてもいいし慣れるとこっちが良い
• IRCボットとかだと、定時発言などもこれでできる
• perldoc AnyEventをみたり、CPANでAnyEventで作られたモジュールを見たりして少しずつ覚えていくといい
AnyEvent::Mac::Pasteboard
• Macのクリップボード(ペーストボード)を監視して、変更があったら指定の処理をするモジュールを書いてみた
• これもタイマーで変更を定期監視しているだけ
• Cocoaの知識があればもっと良い感じができるかも
• その後AnyEvent::Clipboardも作りました(GitHub止まり)
その他• CPANで「Async」というキーワードで検索してみる
• AnyEventを入れるほどではないけど、非同期アクセスが必要な場合に良い場合がある (HTTP::Asyncとか)
• MojoliciousであればMojo::IOLoopとか
• Mojolicious飲み会とかやりたいと思っているので、続きはそこで非同期飲み会やりましょう
注意点とか• LWP::UserAgent等で長時間処理をブロックしたりすると、AnyEventの処理全体をブロックすることになるのでノンブロッキングなAnyEvent::HTTPなどを使おう
• JavaScriptのAjaxやっている人だとコールバック的
• ドヤ顔で「AnyEventで(ry」とか言ってIO::Socket::INET使っていたりすると、ブロッキングとか言われて恥ずかしい思いをします
最後にいろいろ• ノンブロッキングのネットワークI/Oについては
AnyEvent::Socketをベースに作ります
• AnyEvent::SocketはSocketモジュールをベースに、ノンブロッキングネットワークI/Oの方法論を元に書かれているようです
• 基本的に、既に作られているL7層のモジュールを元に、自分で新しいプログラムやモジュールを作ればしあわせ
全然掘り起こせなかったですが 奥が深いので続きはどこかで
おしまい