Hatena::ブログ(Diary)

Logical and Creative i18n RSSフィード

2010-02-06 Google Voiceまとめ

[][]Google Voiceについてまとめてみた


D

概要

Google VoiceとはGrandCentralという会社が行っていたウェブベースの電話管理サービスを買収後進化させた総合電話関連サービス*1

一時期Google VoiceiPhoneアプリAppleかAT&Tが拒否した疑惑とかで話題になったアレ*2

基本的には

が主な機能かと思われる。重要なのがこれがWeb上で出来るということ。

今現在(2010年2月)ではInvite Onlyで, 招待後Web上でアメリカの電話番号を取得(もしくは現在持っている電話番号を紐づける)するという流れ。

実際にエリア毎に番号が割り振られているらしく,自分が住んでいるSan Franciscoの番号(エリアコード415)は一時期不足していた時期もあったみたい。

電話機能の統一

Google Voiceで番号を取得したらその番号に対して自分の持っている電話を紐付ける。

例えば

など。

そしてGoogle Voiceにかかってきた電話に対してそれらの電話に着信を転送することが可能。相手は自分がどこに居るかを意識せずに一つの電話番号で電話出来る。

相手によって転送する電話をカスタマイズすることも可能。例えば家族だったら携帯と家, 会社の取引先は会社の電話だけとかグループ分けも出来る。

また, 例えばもし携帯の電話番号を変えたとしてもその電話番号をGoogle Voiceに登録し直せば全て元通り。ナンバーポータビリティなんかより遥かに拡張性あり。

要は一生使える電話番号が無料で簡単に手に入るということ。もちろん電話番号を変更することも可能だが$10かかる。

国際電話(送信)を安く

International calling rates : International calls - Google Voice Help

Google Voice=>日本の国際電話については現在(2010年2月)段階では

Japan      $0.02/1min

Japan - Mobile $0.11/1min

という破格の値段。例えば実家の電話で親と1時間しゃべっても$1.2, およそ120円ぐらいしかかからない。

SkypeOutよりも安くかけられる感じ*3

ちなみに今持っているAT&TのiPhoneで日本にかけたら$3.5/1minぐらいかかる。

(よく考えてみたらdocomoの国内電話の料金プランで一番時間当たりの通話料が安くなるプランでも30秒7.875円かかる。ちなみに自分は一番安いプランなので30秒21円, 1分にしたら42円で$に直したら$0.42ぐらいだから1/4ぐらいの値段....なんか理不尽な気がしてきた...)

Creditの追加($10,$25,$50)はGoogle Checkoutを通して一瞬で出来る。

International Callのやり方は

D

Making international calls : International calls - Google Voice Help

を見れば分かると思う*4

ちなみに国際電話全般の話をすると

アメリカ=>日本の国際電話

011(アメリカ国際電話識別番号)-81(日本の国コード)-3-xxxx-****

(03-xxxx-****の場合, 最初の0は入れない。携帯だと90-..とか80-..とか)

日本(au以外?)=>アメリカ国際電話

010(日本の国際電話識別番号)-1(アメリカの国コード)-415-xxx-****

(415-xxxx-****の場合)

みたいな感じ*5

日本=>Google Voiceについては分からないがあくまでアメリカの電話番号であることを考えると普通の国際電話をかけるのと同じ値段がかかると思われる。

便利なVoicemail機能

電話に出れなかった場合留守電にメッセージを残してもらうことになると思うがその時にGoogle Voice側で自動的に文字に起こしてそれを通知してくれるという機能。

もちろん音声でも確認可能。Gmailのようなインターフェースなのでかなり使い易い。またSMSなどにも通知可能。

GmailのLabsの機能でGoogle VoiceのNotification Mailからそのまま音声で確認出来たりする。

電話機能のクラウド

クラウド化というのもどうかとは思うけど要は全てがWeb上に記録されるということ。

今までは電話帳, SMS, 通話履歴など全てその電話端末のみに記録されていたものを全て検索可能なGoogleのサービス上に持つことが出来る。

特に自分的に嬉しいのが電話帳がWeb上で管理出来るということ。日本を離れてもわざわざdocomoの携帯を持っているのは電話番号とメールアドレスの維持と連絡先の保持というのが主な目的。

その連絡先を全てGoogleで管理出来ればそれをMacとかと同期するのは簡単だろうし一生使えるContact Listになる。*6

Google Apps対応

将来的にGoogle VoiceはAppsに同袍される予定だという。今もすでに超絶便利なGoogle Appsに電話機能がついたらかなり驚異的になる気がする。通話履歴の管理とか電話番号(ひいては内線番号?)の管理とかかなり簡単になる。もはや「◯◯さんの席ってここでしたか?あ、間違いました」みたいなやり取りが一人一つのGoogle Voice for Appsを持つことで完全になくなる気がする。

疑問

現在はアメリカ限定のこのサービス。もちろん将来的にはアメリカ以外の展開を考えていると思いつつどうやってやるんだろうという疑問が。

例えば日本でサービスが展開されるとして東京03の番号を取得したとする。もちろんそれはGmailアカウントに紐づけられるとすると自分の場合二つの番号を同時に持つことになるがどうやって使い分けするのか....

そもそもそれが実現したら国際電話という概念さえ不要になる気がする。

もし1Gmailアカウントにつき1Google Voice Numberだとすると$10払って電話番号をかえることになるということか...それはそれでしょうがないかという気もする。

もし日本で展開されるとしたら電話番号はGoogle Voice, メールアドレスGmailに統一, 連絡先はWebから自動的に同期でFA

まとめ

アメリカに居る人はとにかく使ってみてその便利さを体験するのが一番だと思う。ちなみに自分はInvite残り2持ってます。

参考リンク

Google Voice

Google Voice - Wikipedia, the free encyclopedia

新料金プラン | 料金・割引 | NTTドコモ

International Calling - Call Rates - Skype

International Long Distance - Wireless from AT&T

国際電話 - Wikipedia

それでも残るAppleへの疑念−iPhoneの「Google Voice」承認問題 - Enterprise Watch

015 10分で理解するGoogle Voice - Googleが電話を変える - EC-One ナレッジセンター レスキューサービス ブログ

電話を革新する『Google Voice』に、通信市場が制覇される? | WIRED VISION

*1:このようなサービスをTelephone Management System(TMS)と呼ぶらしいが比較的新しい分野らしく自分も初耳

*2:最終的にはHTML5ベースのWeb上でiPhoneからも使用可能になっている。

*3Skypeについてはもっと調べた方が良さそうだがGoogle Voiceの方がシンプルな印象

*4:関係ないけど最近YoutubeにCCがついたのに感動した

*5:ググッてもなかなか出てこなくて最初苦労した気がする

*6FacebookのContact listもかなり魅力的だがあくまで情報は相手が公開する必要がある

2010-01-08

[]2010年にやるべきこと

明けましておめでとうございます。久しぶりのブログ更新です。

アメリカBay Areaに来てもうすぐで9ヶ月。

なんとか激動の1年を乗り越え社会人3年目を今年迎えます。

これからやるべきことを整理する意味でも公開して後に引けないようにする意味でも2010年のTODOをここに残したいと思います。

エンジニアとして

仕事を100%やり切る

今関わっているプロジェクトは世界的に見てマジでアツイプロジェクトだと客観的に見ても感じています。少し具体的に言うとiPhone関係の仕事ですが、単なるアプリケーションを超えたプラットフォームの構築に関わっています。仕事の中でやるべき事は日々変わっていくのであえて決まった目標などは立てませんが、その時々で担当するプロジェクトに全力で取り組みたいと思います。

とりあえず仕事上必須なObjective-Cとかその周辺の技術はしっかりキャッチアップしなきゃですね。

コンピュータサイエンスの基礎を学ぶ

大学時代工学部に所属していたもののプログラミングの授業を真面目に受けたことはなく、システム創成学科という不思議な学科に入った後も、研究室に入るまではプログラミングの課題に関して友達に頼りっきりでした。そんな自分には恥ずかしながらコンピュータサイエンスの基礎が身についていないと最近感じるようになりました。

コンピュータサイエンスといっても色々な分野があると思いますが、まず最初は「データ構造とアルゴリズム」から少しずつ洋書の教科書を読んでいって独学していきたいと思います。

とりあえずは「Introduction to Algorithms」から始めてSICPやその他OSプログラミング言語などの理解に進んでいきたいと思います。

参考: http://leoclock.blogspot.com/2009/09/blog-post_21.html

C++をマスターする

仕事でもObjective-C++を書いていますが、個人的に今年は本格的にC++をマスターしたいと思います。

やっぱりエンジニアたる者JavaとC(or C++)は書けるようになりたいっ...!

とりあえず「C++ Primer Plus」から始めて徐々に中規模のソフトウェアを書けるようになりたいと考えてます。

プライベート

音楽をちゃんと聴く

今までは音楽をなんとなく聴いていたんですが、去年末にAppleのSPINというDJ用のハードを衝動買いしたのもあって、DJちょっとやってみたいなあと思っています。DJやるにはまずちゃんと音楽聴かないとなあっていうのが今感じているところで、これからは曲名とアーティストと(あとはアルバム名とか?)をちゃんと覚えつつ曲調とかスピードとかを意識しながら聴いていこうかあと思っています。

Bay Areaで色々な人に会う

Bay Areaに来て2年目になる年ですが、10,20年居られる訳ではなくいつかのタイミングで日本に帰ることは会社的に決まっているので、今のうちに色々な人に会って話を聞いておきたいという思いがあります。日本に戻ったらこちらにいつ戻れるか(もしくは一生戻れないか...)分からないので、あとになって後悔しないようにしたいですね。

とりあえず

以上ですかね。なんか漠然とした目標になっちゃいましたが、毎月のタイミングでそれぞれをブレイクダウンしてしっかりとやっていきたいと思ってます!

それでは本年もよろしくお願いいたします。

2009-10-09 Perl非同期プログラミング入門 - 1

[][]Coro::Intro和訳

ちょっと波に乗り遅れた感じもしていますが,Coroの5.2もリリースされたことだし,今まで勉強してきたことの復習も兼ね, Coro::Introを訳してみる.

http://search.cpan.org/~mlehmann/Coro-5.2/Coro/Intro.pod

訳やそもそもの理解が間違っていたら是非コメントとかで教えてくださいm(__)m

どうでもいいけどマジでインデントがスペース3つだ...w

Coroの紹介

これはCoroとCoroのサブモジュールの重要な機能について紹介するチュートリアルです.

まず基本的なコンセプトを紹介し,その後にそれらの簡単な使い方を紹介します.

Coroって何?

Coroは初期の段階ではコルーチン*1と呼ばれるファーストクラスの継続の特定の要件を実装したシンプルなモジュールとして作られ始めた.

これは基本的には,現在の実行ポイントを保ったまま他のポイントに実行を移しつつ,いつでも先ほどの実行ポイントに戻ってくることができる,Cでいうsetjmp/longjmpのようなものである. この機能は今はCoro::Stateにある.

これらを応用したものがスケジューラや協調スレッドであり,現在のCoroのメインの使われ方である. にも関わらず,多くのドキュメントや書き方がコルーチン, だいたい単に'coro'と呼ばれるこれらのスレッドを参照している.

スレッドは最小限の機能を持ったPerlインタープリタ, もしくはプロセスにとても近い. 全ての機能を備えたインタープリタプロセスと違って、スレッドはそれ自身の変数名前空間を保っていない, つまり全ては共有されている。これが意味するのは, あるスレッド変数(もしくはリファレンスのようなあらゆる値)を変更した場合,他のスレッドが同じ変数やロケーションを見ている時はすぐにこの変更は反映される,ということである.

協調とはこれらのスレッドCPU使用の観点においてお互いが協同する, つまりただ一つのスレッドCPUを占有している時、もしもう一つのスレッドCPUを使いたい場合、実行されているスレッドCPUを明け渡すということである. 後半の動作はそのための関数を呼んで明示的に行う場合とセマフォ*2I/Oリクエストの完了などリソースを待って暗示的に行われる場合の両方がある.

Perl自体が比較的混乱を招く表現を使っている - Perlの中でスレッドと呼ばれているものは,実際,他のところではプロセスと呼ばれているものである.

'Perlスレッド'と呼ばれるものは実際はWindowsで使われているUnixプロセスエミュレーションのコードを加工したものである.

したがって,これらは実際にはスレッドではなくプロセスなのである. もっとも大きな違いは変数(あとはコードも)プロセス間では共有できないことである.

協調スレッド

協調スレッドはCoroが提供する機能で:

use Coro;

とすることで使えるようになり,スレッドを作るにはasyncというCoroモジュールから自動でexportされる関数を以下のように使う:

async {
   print "hello\n";
};

asyncは最初の引数にコードブロックを取る(間接オブジェクト構文)

さらに引数を渡すこともでき,それらはコードブロック実行時に@_に入るが、コードブロック自体がクロージャなので単に現在あるレキシカル変数を参照することも出来る.

上記のコードを保存してPerlプログラムとして実行しても,何も結果は得られない.

なぜかというと、スレッドを作って実行可能状態*3になっているが(asyncはready queueと呼ばれるところにスレッドを置く),実際にはmainのプログラム(他のと大体同じスレッドの一つ)がCPUを明け渡さずに,ファイルの最後に到達しそのままプログラム自体を終了したため, そのスレッドCPUに処理されなかったからである.

明示的にCPUを明け渡すにはcede(他のスレッドの実装だとyieldと呼ばれるもの)という関数を使用する:

use Coro;

async {
   print "hello\n";
};

cede;

上記を実行すると「hello」と表示されてプログラムは終了する.

これだと面白くないのでもう少し面白いプログラムを見てみる:

use Coro;

async {
   print "async 1\n";
   cede;
   print "async 2\n";
   cede;
};

print "main 1\n";
cede;
print "main 2\n";

これを実行すると:

main 1

async 1

main 2

async 2

という結果が得られる.

この結果はスコープを超えたジャンプができることをよく表している。 つまりmainのプログラムで最初の行を表示し,CPUを他のスレッドに渡し,そこにあったスレッドが"async 1"を表示しCPUを明け渡す. そこで有効な他のスレッドはmainのプログラムなので,そのまま同じように続けて実行される.

もっと詳しく言うと, asyncは,新しいスレッドを作るが,その時作られた全ての新しいスレッドは待機状態になる. そして,それを動かすためには,ready queueにそのスレッドが置かれる必要があり, これがasyncが行う2番目の動作である. スレッドCPUを明け渡す時, Coroは常にschedulerと呼ばれるものを実行する. スケジューラはready queueから次のスレッドを選択し,queueから外し実行する.

cedeも同様に二つのことを行う.まず実行中のスレッドをready queueに退避させ,ready queueに置かれているスレッドをスケジューラの中に入れる. これによってCPUが明け渡されて,最終的にそのスレッドが実行される.

つまり, cedeはこのように実装されていることになる:

sub my_cede {
   $Coro::current->ready;
   schedule;
}

これは実際に動作する, というのは$Coro::currentが常に実行中のスレッドを含み,スケジューラ自身がCoro::scheduleによって呼ばれるからである.

ただ単にscheduleを呼ぶだけだと何が起こるか? シンプルだ. スケジューラは次のreadyなスレッドを選択し実行する. もしready queueに現在のスレッドが入っていない場合,何かがそれを起こすまでsleepする.

次の例が現在のスレッド変数として記憶し,新しいスレッドを作り,mainのプログラムをsleepさせるというものである.

新しく作られたスレッドはランダムにreadyメソッドを呼ぶことで,mainのスレッドを起こしたり起こさなかったりする:

use Coro;

my $wakeme = $Coro::current;

async {
   $wakeme->ready if 0.5 < rand;
};

schedule;

これを実行すると,2回に1回はasyncがmainのプログラムを起こしそのままプログラムは終了するが,

FATAL: deadlock detected at - line 0

というメッセージが表示される場合もある.

なぜこれが起こるのか? asyncスレッドが最後まで到達した場合, Coro::terminateが呼ばれることによってスレッドは終了し,再度スケジューラが起動する. asyncスレッドはmainスレッドを起こしていないし,他にスレッドは存在しないので,何も起動するものがなくプログラムは実行継続が不能になる. 実行することが出来るスレッドはある(mainスレッド)が,実行可能状態になっているスレッドはないので, Coroはdeadlockのシグナルを送り,プログラムは強制終了する.

実際には,プログラムを続けるようにする重要なケースがある, これがイベント駆動プログラムである. そのような場合,プログラムタイムアウトやソケットを通したデータなど外部からのイベントを待つ必要がある.

このケースではdeadlockが起こるのは望ましくないので,Coro::AnyEventという名前のモジュールスレッドをイベントループにする. Coro::AnyEventによって,Coroにおいて,エラーメッセージと共にdieする代わりに,スレッドを起こすイベントを受け取る可能性を持ったイベントループが動くようにする.

セマフォとその他のロック機構

ready,cede,scheduleのみを使ってスレッドをシンクさせることは難しい, 特に同時にたくさんのスレッドがreadyになっている場合は. Coroはより簡単にスレッドをシンクさせることが出来るモジュールをいくつかサポートしている. そのようなモジュールのうち最初に紹介するものはCoro::Semaphoreというモジュールで,カウントできるセマフォを実装している(バイナリセマフォはCoro::Signalによって使うことが出来る):

use Coro;
use Coro::Semaphore;

my $sem = new Coro::Semaphore 0; # a locked semaphore

async {
   print "unlocking semaphore\n";
   $sem->up;
};

print "trying to lock semaphore\n";
$sem->down;
print "we got it!\n";

このプログラムはロックされたセマフォ(変数0のセマフォ)を作り,それをロックしようとする. セマフォはすでにロックされているのでmainスレッドセマフォが有効になるまでブロックされる.

これによって,CPUはasyncスレッドに明け渡され,セマフォはロックを解除される(そしてすぐにreturnして終了する)

ここでセマフォは有効になったので,mainのプログラムセマフォをロックし実行される.

セマフォリソースのロックをするため, もしくは他のスレッドリソースアクセスしたり使うことを防ぐために使われることが多い. 例えば,とても時間のかかる関数(一時的に大量のメモリをアロケートする)を考えた場合, この関数の中で大量のスレッドを呼ばないようにしたいと考えられる. そこでセマフォを使い,以下のように書くことが出来る:

my $lock = new Coro::Semaphore; # unlocked initially

sub costly_function {
   $lock->down; # acquire semaphore

   # do costly operation that blocks

   $lock->up; # unlock it
}

これでどれだけ多くのスレッドからcostly_functionが呼ばれても,ひとつのスレッドでしかcostly_functionの中の処理は行われず,その他のスレッドは$lockがdownメソッドを呼ぶまではcostly_functionの処理を待つことになる.

コメントの中でなぜ"operation that blocks"(ブロックされた処理)と言及しているか? それはCoroのスレッドは協調し,costly_functionがCPUを明け渡そうとしない限り,他の制御されているスレッドは単に動かないからである. 関数自体がCPUを明け渡すことをしない場合ロック機構は無駄になってしまうが,現実世界を鑑みると,そのようなことは滅多に無い.

次にdownメソッドを呼んだ後にdieしてしまうコードを実行する時を考える. この場合セマフォはロックされたままになり,普通は欲しかった結果にならない. その場合たいていロックをもう一度解除したいだろう.

ここではguardメソッドを使う:

my $lock = new Coro::Semaphore;

sub costly_function {
   my $guard = $lock->guard; # acquire guard

   # do costly operation thats blocks
}

このメソッドはセマフォをdownしてguardオブジェクトと呼ばれるものを返す. このオブジェクトへのリファレンスがある限りは何も起こらないが,リファレンスが消えると,例えばcostly_functionが例外を返すか投げたとすると,忘れず自動的にセマフォのupメソッドを呼ぶ.他のスレッドがそのスレッドに対してcancelメソッドを送る時でさえguardオブジェクトはロックを解除する.

Coro::SemaphoreとCoro::Signalの他に,読み書きできるロック(Coro::RWLock)とセマフォのセット(Coro::SemaphoreSet)もある.

チャンネル

セマフォは使える,しかしデータの交換の時も同様にスレッド同士を通信させたい時もある.この場合Coro::Channelが使える.チャンネルはCoroにおけるUnixパイプ(かAmiga*4のメッセージの移植版にも非常に似ている)であり,片方にデータをputすると,もう一方でそのデータをread出来る.

ここにスレッドを作り,そこに数字を送る簡単な例がある.スレッドは数字の2乗を計算し他のチャンネルにそれをputする,そうするとmainのスレッドがそこから計算結果を読む:

use Coro;
use Coro::Channel;

my $calculate = new Coro::Channel;
my $result    = new Coro::Channel;

async {
   # endless loop
   while () {
      my $num = $calculate->get; # read a number
      $num ** 2; # square it
      $result->put ($num); # put the result into the result queue
   }
};

for (1, 2, 5, 10, 77) {
   $calculate->put ($_);
   print "$_ ** 2 = ", $result->get, "\n";
}

結果は:

1 ** 2 = 1

2 ** 2 = 4

5 ** 2 = 25

10 ** 2 = 100

77 ** 2 = 5929

getメソッドとputメソッドは現在のスレッドをブロックする.つまりgetはまず有効なデータがあるかどうかをチェックし,もし無い場合現在のスレッドをデータが届くまでブロックする.putメソッドも同様にスレッドをブロックできる,というのはチャンネル一つ一つは「最大バッファ許容数」を持っていて,特定数以上はデータを保持できなくなっている.これはチャンネル作成時に設定できる.

上記の例ではputはスレッドをブロックしない,というのはデフォルトのチャンネルの許容数はとても高いからである.なのでまずforループの中でデータがチャンネルに置かれ,結果をgetしようとする.asyncスレッドはまだ何もデータを置いていないので(最初のイテレーションが動いてさえいない),resultチャンネルはまだ空で,mainスレッドはブロックされる.

この時点で実行可能状態のスレッドは2乗するスレッドなので,それが起こされ,数字を取得し2乗した後resultチャンネルにその結果を置く.そしてmainスレッドがまた起動する.他のスレッドを起こしてready queueに入れるまで動き続ける.それ以上でもそれ以下でもない.

asyncスレッドがcalculateチャンネルから次の数字を取ってこようとする時だけmainスレッドはブロックされ(そこに何もないから),あとは動き続ける.

一般的に,Coroはしなければいけない時のみスレッドをブロックする. Coroモジュールやサブモジュールがしなければいけない場合CPUを明け渡す.というのはCoroとサブモジュールはイベントが起こるのを待つからである.

しかし,複数のスレッドが$calculateに数字を置いたり$resultから結果を取ってこようとする時,どの結果が誰のものかが分からないということに気をつけなれけばならない.これに対する対処法はセマフォを使うか,数字を送るだけでなくプライベートなresultのチャンネルを送るというものがある.

Coro::Channelはある一定量のデータをバッファできる.ソースコードを読むのはとても有益である.それはとてもシンプルで,同期するために内部的に2つのセマフォを使っている.

どれが自分のもので, どれが共有のもの?

正確には何がスレッドを構成しているのか?はっきりしているのは,それが現在の実行ポイントを含んでいるということである. そこまで明確ではないが,それはレキシカルな変数は全て含まなければならない,つまり全てのスレッドは自身のレキシカル変数を持つ.なぜこれが必要なのかを理解するために,このプログラムについて考える:

use Coro;

sub printit {
   my ($string) = @_;

   cede;

   print $string;
}

async { printit "Hello, " };
async { printit "World!\n" };

cede; cede; # do it

上記の結果はHello, World\nになる.もしprintitがスレッド毎の変数を持っていないとすると多分World!\nWorld!\nになるだろう.これはおそらく期待通りの動きとはちょっと違うし,スレッドを有効に使うことが難しくなってしまうだろう.

スレッド毎にもっている他のいくつかのものは,

$_,@_,$@と正規表現変数である$&,%+,$1,$2

$_はローカル変数のように使えるしスレッド毎にローカライズされている.正規表現の結果も同じである($1, $2など)

@_はレキシカルのように引数を含み,それはスレッド毎にある

$@はスレッド毎になっている必要は必ずしもないが,とても有用である

$/とデフォルトの出力のファイルハンドル

スレッドI/Oの時にブロックされる時が多い$/は行を読む時に使われるのでもし共有になっていたらとても使いにくいのでスレッド毎になっている

デフォルトの出力のハンドル(selectを参照)は難しいケースである.時々グローバルになっていた方が使いやすいし,スレッド毎になっていた方が使 いやすい場合もある.スレッド毎になっている方がよくあるように見えるのでスレッド毎になっている.

$SIG{__DIE__}と$SIG{__WARN__}

これらがスレッド毎に無い場合,よく使われる:

eval {
   local $SIG{__DIE__} = sub { ... };
   ...
};

のような構造ではコルーチンのスイッチが出来なくなる.例外処理スレッド毎になっているのでこれらの変数スレッド毎であるべきだ.

他の多くの難解なもの

例えば$^Hはスレッド毎になっている.ほとんどのスレッド毎の状態はperlに対してはっきりと見えていないが,インタープリタは動く必要がある.通常はこれらを気にしないだろう.

他の全てのものはスレッド間で共有される.例えばグローバルの$a,$bは共有される.いつそれが活きるか?sort関数を使ったとき,これらの変数は特別になり,ソートする時スレッドがスイッチしたら驚く結果が得られるだろう.

他の例としては$!やerrno,$.,現在の行数,$, ,$\,$"と他の特別変数である.

いくつかの場面ではスレッドに対してローカライズされた方がいい変数もあるが,そんな使い方はまずないし,ローカライズも難しい.

問題がある場合は将来のバージョンのCoroでより多くのスレッド毎の状態を持つかもしれない.

デバッグ

時々それぞれのスレッドがやっていること(や,最初にどのスレッドがあるか)を知ることは有効である.Coro::Debugモジュールが(他にも使える機能がある中でも)psコマンドのようなリストを出力してくれる機能を持っている:

use Coro::Debug;

Coro::Debug::command "ps";

$calculate->getの後にこれを動かすとこのような結果が得られる:

PID SC RSS USES Description Where

8917312 -C 22k 0 [main::] [introscript:20]

8964448 N- 152 0 [coro manager] -

8964520 N- 152 0 [unblock_sub scheduler] -

8591752 UC 152 1 [introscript:12]

11546944 N- 152 0 [EV idle process] -

面白い - 思っていたより多くのものが裏では動いている. 他のスレッドを無視すると,mainスレッドのPIDは8917312でasyncによって起動されたものはPID8591752である.

後者はDescriptionを持っていないただのスレッドである. なぜDescriptionを持っていないかというとただ単に設定していなかっただけだからである. 設定するのは簡単で,このように$Coro::current->{desc}にそれを設定すればいい:

async {
   $Coro::current->{desc} = "cruncher";
};

これはプログラムのデバックの時やCoro::Debugによるインタラクティブデバッグシェルを使う時に比較的有効である.

実際の使い方 - イベントループ

実際にはCoroはイベントループを使うプログラムで動かしたい.事実,Coroのスレッドが使われているほとんどの実際のプログラムはイベント駆動とスレッドをベースにしたテクニックの組み合わせで書かれてあり,両方の世界の一番をCoroと一緒に理解するのが簡単である.

CoroはAnyEventによってサポートされているイベントループと統合できる.それにはCoro::AnyEventを単に使うだけでEVやEventモジュールを活用することが出来る.

ここにAnyEventのイベントループがつくる全て(Coro::Socketが自動的にすべて初期化する)を利用したシンプルなfingerクライアントがある:

use Coro;
use Coro::Socket;

sub finger {
   my ($user, $host) = @_;

   my $fh = new Coro::Socket PeerHost => $host, PeerPort => "finger"
      or die "$user\@$host: $!";

   print $fh "$user\n";

   print "$user\@$host: $_" while <$fh>;
   print "$user\@$host: done\n";
}

# now finger a few accounts
for (
   (async { finger "abc", "cornell.edu" }),
   (async { finger "seboo", "world.std.com" }),
   (async { finger "trouble", "noc.dfn.de" }),
) {
   $_->join; # wait for the result
}

新しくでてきたものはここにはあまりない.第一にCoro::Socketである。このモジュールは,コルーチンのようになっていることをのぞき,IO::Socket::INETと同じように動く.

一方で,Coro::Socketはネットワークを待つときCPUを他のスレッドに渡すやり方を知っているので,並列処理が可能である.

他の新しい事柄はjoinメソッドである.このサンプルでやりたいことは3つのasyncスレッドを起動し,役割が終わった時に終了するというだけである.セマフォを使っても実現できるが,terminateを実行するのを同調しながら待つ方が簡単で,それをするのがまさにjoinメソッドである.

3つのasyncスレッドがjoinした順序と違う順序で終わるかもしれないが,これは重要ではない.スレッドがまだ起動しているときjoinは単に待っている.スレッドがすでに終了していれば,スレッドは結果のステータスを単に取ってくる.

もしイベント駆動プログラミングの経験があれば,上記の問題はいくつかの作業を始めてイベントループ(EV::loop)をまわす普通のパターンとはまったく違っていることが分かる。

事実,Coroを使っている時もこのパターン沿う重要なプログラムがあり,EVを使ったCoroのプログラムは以下のようになる:

use EV;
use Coro;
use Coro::AnyEvent;

# start coroutines or event watchers

EV::loop; # and loop

実際,デバッグするために以下のようなことをすることもある:

use EV;
use Coro::Debug;

my $shell = new_unix_server Coro::Debug "/tmp/myshell";

EV::loop; # and loop

これによってプログラムは起動し,/tmp/myshellのUNIXドメインソケット上にインタクティブシェルも起動する.socatプログラムでこれにアクセスすることが出来る:

# socat readline /tmp/myshell

coro debug session. use help for more info

> ps

PID SC RSS USES Description Where

136672312 RC 19k 177k [main::] [myprog:28]

136710424 -- 1268 48 [coro manager] [Coro.pm:349]

> help

ps [w|v] show the list of all coroutines (wide, verbose)

bt <pid> show a full backtrace of coroutine <pid>

eval <pid> <perl> evaluate <perl> expression in context of <pid>

trace <pid> enable tracing for this coroutine

untrace <pid> disable tracing for this coroutine

kill <pid> <reason> throws the given <reason> string in <pid>

cancel <pid> cancels this coroutine

ready <pid> force <pid> into the ready queue

<anything else> evaluate as perl and print results

<anything else> & same as above, but evaluate asynchronously

you can use (find_coro <pid>) in perl expressions

to find the coro with the given pid, e.g.

(find_coro 9768720)->ready

loglevel <int> enable logging for messages of level <int> and lower

exit end this session

もちろんMicrosoft犠牲者でも,全然セキュアではないが,new_tcp_serverを使うことができる.

実際の使い方 - File I/O

ディスクIOはネットワークよりはかなり早い場合が多いが,とは言え,もしCPUが,何かを出来るならば他のことが出来るぐらい長い時間がかかる.

幸運にもCPANのIO::AIOモジュールを使ってIOのコールをバックグラウンドに移し,フォアグラウンドでは有益な処理をすることができる. これはイベント-コールバックベースであるが,Coroはこれに対するCoro::AIOというインターフェースを持っていて,スレッドの中で自然にその機能を使うことが出来る:

use Fcntl;
use Coro::AIO;

my $fh = aio_open "$filename~", O_WRONGLY | O_CREAT, 0600
   or die "$filename~: $!";

aio_write $fh, 0, (length $data), $data, 0;
aio_fsync $fh;
aio_close $fh;
aio_rename "$filename~", "$filename";

これは新しいファイルを作り,データをそれに書き込み,ディスクと同期し,新しいコピーをアトミックにベースファイルと入れ替える.

他のモジュール

このイントロダクションでは少しのメソッドとモジュールにしか触れていないが,Coroは他にも多くの機能(Coroのmanページを参照)やモジュール(CoroのSEE ALSOセクションに書かれている)を持っている.

特筆すべきモジュールはCoro::LWP(並列LWPリクエスト,しかしAnyEvent::HTTPを見た方がいいが,これはより限定的で代わりになりえる), 非同期のデータベースが必要な時はCoro::BDB, コルーチンの中でファイルハンドルを使う必要がある場合(STDINやSTDOUTへのアクセスがよくあるが)はCoro::Handle,EV(Coro::AnyEventによって自動的に使われる)へのインターフェースとして最適化されているCoro::EV,である.

作者

Marc Lehman*5

http://home.schmorp.de/

*1:Co-routine: いったん処理を中断した後続きから処理を再開できる処理単位. 協調的処理やイテレータ,継続状態をもつプログラムを書く時に用いられる

*2:Semaphore: 排他区間を確保し,資源に同時アクセスできる上限を規定したい場合に用いる同期機構

*3:readyの訳

*4Amiga OSか女友達か分からなかったorz

*5メールアドレスは一応自重した

2009-10-05 Google Wave Previewの件

[]Google Waveの罠

Google Wave previewがはじまったお。実はsandboxアカウント持ってる人は既に使えるワナ - a2c.get.diary

にも書かれてある通り,Google I/OとかGoogle Developer Dayでsandboxのアカウントをもらっている人に対しては,wavesandbox.com宛にすでに

Enable your Google \/\/ave account

っていうタイトルのwaveが来てて,そこからActivationへのリンクが辿れるとのこと。

自分もwave.google.comに直接メールアドレス登録したり,友達にinvitation listに登録してもらったりしたけど一向に音沙汰が無いのにヤキモキしてたので,この情報,とても助かりました. Thanks to id:a2cさん!!

にしてもこれはちょっとユーザビリティが悪いと言わざるを得ない感じですよねえ >

日本語入力

a2cさんのブログでは日本語入力できないと書かれてあったけど,自分が試したら出来た. ここもどんどん改良が加えられていくんだろねー。 関係ないけどPreviewってαみたいな意味合いなのかな??

2009-09-03 easy_install jinja2が出来ない時は

[]jinja2がインストールできない

そういえばちょっと前に

% sudo easy_install jinja2
Password:
Searching for jinja2
Reading http://pypi.python.org/simple/jinja2/
Reading http://jinja.pocoo.org/
Best match: Jinja2 2.1.1
Downloading http://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.1.1.tar.gz#md5=b37fc262e4f613eec57c3defe6aea97c
Processing Jinja2-2.1.1.tar.gz
Running Jinja2-2.1.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-4_zfAi/Jinja2-2.1.1/egg-dist-tmp-izEPGg
warning: no files found matching 'Makefile'
warning: no files found matching 'ez_setup.py'
warning: no previously-included files matching '*' found under directory 'docs/_build/doctrees'
No eggs found in /tmp/easy_install-4_zfAi/Jinja2-2.1.1/egg-dist-tmp-izEPGg (setup script problem?)

てな感じでエラーが出て悩んでて

#pocoo@freenodeとかで質問して華麗にスルーされてorz

最終的に

sudo easy_install -U setuptools
sudo easy_install jinja2

ってやるとインストールできた。