什么是面向事件的編程(事件驅(qū)動(dòng)的編程):
編程中所有的程序是由事件決定 – 可以是由用戶操作(鍵盤,鼠標(biāo)),也可以是由其他程序和流的到達(dá)或者操作系統(tǒng)事件(如網(wǎng)絡(luò)數(shù)據(jù)包到達(dá))來觸發(fā)執(zhí)行.
面向事件編程可以也被定義為,寫一個(gè)計(jì)算機(jī)程序,在其中的代碼(通常程序的功能的頭部)被明確分配應(yīng)用程序的主回路,其代碼本身由兩部分組成方法:事件和事件處理的代碼。
面向事件的編程通常被應(yīng)用在三種情況下:
1.創(chuàng)建用戶界面的控制(包括圖形)
2.創(chuàng)建一個(gè)基于服務(wù)器的應(yīng)用程序
3.游戲編程時(shí)多個(gè)對(duì)象的管理
我們系統(tǒng)管理時(shí),這種應(yīng)用在服務(wù)器的應(yīng)用程序中使用面向事件的編程很多,比如用于服務(wù)器應(yīng)用解決10,000個(gè)并發(fā)連接(所謂 c10k 問題)
anyevent 是一個(gè)性能非常好的基于事件驅(qū)動(dòng)的程序,有人使用它來解決 c10k 的問題,象平時(shí)我們寫的程序,都是基于過程.我們都是先做完事件1-> 然后做事件2->然后做事件3 .這種方式.
但基于事件就完全不一樣了,在主流程中你基本只有一個(gè)主體框架,程序的動(dòng)作觸發(fā)都是由事件來驅(qū)動(dòng).比如我們使用的窗口程序.點(diǎn)最大化最小化,都是基于事件,當(dāng)接收到了最大化的事件做最大化事件那部分的程序開始運(yùn)行.不在從頭到尾部來執(zhí)行.所以我們讀基于事件的程序,最好是畫成思維導(dǎo)圖來幫助我們理解.
基于事件的程序常用到的最大好處是用來做異步,例如,我們要下載 100 個(gè)文件,下載完后對(duì)這些文件進(jìn)行處理.可能給每個(gè)下載和處理的過程寫成事件,這些事件可以同步運(yùn)行(關(guān)鍵在于網(wǎng)絡(luò)連接和進(jìn)行文件的讀寫 io 時(shí)要等待,事件是給這些等待復(fù)用起來).
不知大家了解 perl 中的 select 這個(gè)功能不,就是等到句柄可以讀或者寫的時(shí)候,做不同的讀或者寫的操作.事件循環(huán)也是一樣.
在整個(gè) anyevent 入門中,我們只要關(guān)注二個(gè)點(diǎn)就行, watchers(監(jiān)控者) 和 條件變量.
watchers(監(jiān)控者)
在 select 中,有個(gè)角色叫監(jiān)控者,就是 select 函數(shù)本身.
在 anyevent 中不但可以監(jiān)控 io 還可以監(jiān)控別的一些事件.來做不同的處理.我們可以看成這是不斷的盯著某件事情的人
有如下幾個(gè)基本的內(nèi)置的可以用來盯著的事情(監(jiān)控者).
timer : 監(jiān)控時(shí)間,到了一定的條件,然后對(duì)不同的時(shí)間做不同的事件
i/o: 這個(gè)是監(jiān)控到 io 是否可以讀寫,然后做相應(yīng)的事件
idle: 空閑時(shí)做什么事件
signal : 監(jiān)控觀查到不同的信息,調(diào)用相應(yīng)的事件
child process: 對(duì)子程序的狀態(tài)來調(diào)用相應(yīng)的處理事件
timer watchers
基本語法
代碼如下:
anyevent->timer(
after => $seconds, # 多久之后做相應(yīng)的操作.
interval => $seconds, # 在上面條件生效后,每格多久進(jìn)行一次 callback.
cb => $cb, # cb 是 callback 的簡寫,所以知道了吧,只要到了前面的條件,就會(huì)運(yùn)行 cb => 指向的函數(shù).
);
使用實(shí)例:
下面的例子是,5 秒后,每 2 秒進(jìn)行一次 callback 中的事件,直到 $w 這個(gè)注冊的事件被 undef 為止(也就是 $count > 10 次).這個(gè)中的 undef $w 是取消掉這種 watcher 的方法.
代碼如下:
#!/usr/bin/perl
use strict;
use anyevent;
my $cv = anyevent->condvar;
my $count = 0;
my $w; $w = anyevent->timer(
after => 5,
interval => 2,
cb => sub {
$count++;
warn 這是第 $count 次調(diào)用;
if ($count >= 10) {
undef $w;
}
}
);
$cv->recv;
i/o watchers
基本語法
代碼如下:
my $fh = ....; # 打開一個(gè)句柄
my $io; $io = anyevent->io(
fh => $fh, # 上面打開的句柄,也可以是標(biāo)準(zhǔn)輸入和輸出
poll => w, # 這個(gè)地方可以選擇 r 和 w 來表示讀和寫的 io 事件
cb => sub {
syswrite( $fh, 寫入的內(nèi)容 );
undef $io;
}
);
使用實(shí)例:
下面的例子,是使用 io 監(jiān)控到可以讀,就調(diào)用 cb 的函數(shù),直接讀文件 test.txt,每次一個(gè)字節(jié),直到讀完這個(gè)文件就通過 undef 消掉這個(gè)事件.
代碼如下:
#!/usr/bin/perl
use strict;
use anyevent;
my $cv = anyevent->condvar;
open my $fh, <test.txt or die 不能打開文件句柄 $!;
my $io; $io = anyevent->io(
fh => $fh,
poll => r,
cb => sub {
my $len = sysread( $fh, my $buf, 1 );
if ($len > 0) {
print read '$buf'\n;
}
else {
undef $io;
die 讀出錯(cuò): $!;
}
});
$cv->recv;
idle watchers
基本語法
代碼如下:
my $w = anyevent->idle (cb => sub { ... });
使用實(shí)例:
下面的例子,當(dāng)整個(gè)程序中,沒有其它事件在運(yùn)行時(shí),就會(huì)運(yùn)行 idle .它就是當(dāng)其它事件都在等待和空著的時(shí)候,所調(diào)用的.
代碼如下:
#!/usr/bin/perl
use strict;
use anyevent;
my $cv = anyevent->condvar;
my $t; $t = anyevent->timer(
after => 1,
interval => 1,
cb => sub { print time().\n }
);
my $w; $w = anyevent->idle(
cb => sub {
warn idle;
# undef $w;
}
);
$cv->recv;
signal watchers
基本語法如下,就是當(dāng)接收到 posix signal 的時(shí)候,運(yùn)行 callback 中的事件.
代碼如下:
my $w = anyevent->signal (signal => term, cb => sub { ... });
child procrss watchers
基本語法如下
代碼如下:
# child process exit
my $w = anyevent->child (pid => $pid, cb => sub {
my ($pid, $status) = @_;
...
});
條件變量(多個(gè)條件時(shí))
這個(gè)是 anyevent 學(xué)習(xí)上面幾種事件監(jiān)控后必須要了解的.大家都見到上面有 anyevent->condvar; 和 $cv->recv這二個(gè),condvar 是 condition variable 的簡寫.是指當(dāng)什么樣的條件成立時(shí)的變量
其實(shí)就是條件,當(dāng)達(dá)到什么條件時(shí)退出事件循環(huán).所以 anyevent 中沒有傳統(tǒng)事件中的 loop 函數(shù).所以使用條件變量就相當(dāng)于讓事件這個(gè)轉(zhuǎn)起來.
基本的 $cv->recv 是和 $cv->send 成對(duì)出現(xiàn)的,當(dāng)事件調(diào)用 send 時(shí),就一定要有 recv 收到這個(gè)調(diào)用,才會(huì)退出事件.
下面的 $cv->begin 和 $cv->end 也基本是這個(gè)意思.send 是單個(gè)條件.begin 和 end 是多個(gè)條件成立時(shí)退出,換個(gè)語來講,就是這些事件都成對(duì)的完成后,才退出事件.
代碼如下:
#!/usr/bin/perl
use strict;
use anyevent;
my $cv = anyevent->condvar( cb => sub {
warn 調(diào)用結(jié)束;
});
for my $i (1..10) {
$cv->begin;
my $w; $w = anyevent->timer(after => $i, cb => sub {
warn finished timer $i;
undef $w;
$cv->end;
});
}
$cv->recv;
默認(rèn)的 condvar 會(huì)對(duì)事件建一個(gè)條件為假的變量,所以直接有 send 和 begin send 之類才會(huì)變成真,然后退出事件循環(huán).可以給這個(gè)地方看成一個(gè)信號(hào)量來理解就好了.y
如果條件不成立,在 anyevent 中事件會(huì)一直 loop .所以上面的例子中沒有 send .
有關(guān) anyevent 其它,大家入門后可以玩玩象 anyevent::http,twiggy 之類.看看這些應(yīng)用和項(xiàng)目.
另外,在 anyevent 中我們常常使用 ev .他是一個(gè) c 的 libev 的 perl 接口,有非常高的性能.看完上面,在看看下面 ev 的使用,非常容易吧,基本不變.只是沒出現(xiàn)條件變量,
使用的傳統(tǒng)的 ev::loop; 來使這個(gè)運(yùn)行起來.
代碼如下:
use ev;
# timers
my $w = ev::timer 2, 0, sub {
warn is called after 2s;
};
my $w = ev::timer 2, 2, sub {
warn is called roughly every 2s (repeat = 2);
};
undef $w; # destroy event watcher again
my $w = ev::periodic 0, 60, 0, sub {
warn is called every minute, on the minute, exactly;
};
# io
my $w = ev::io *stdin, ev::read, sub {
my ($w, $revents) = @_; # all callbacks receive the watcher and event mask
warn stdin is readable, you entered: , <stdin>;
};
# signals
my $w = ev::signal 'quit', sub {
warn sigquit received\n;
};
# child/pid status changes
my $w = ev::child 666, 0, sub {
my ($w, $revents) = @_;
my $status = $w->rstatus;
};
# stat changes
my $w = ev::stat /etc/passwd, 10, sub {
my ($w, $revents) = @_;
warn $w->path, has changed somehow.\n;
};
# mainloop
ev::loop; # loop until ev::unloop is called or all watchers stop
ev::loop ev::loop_oneshot; # block until at least one event could be handled
ev::loop ev::loop_nonblock; # try to handle same events, but do not block
注:本文中大部分內(nèi)容來自日本的@lestrrat