perl AnyEvent简单介绍和入门知识
什么是面向事件的编程(事件驱动的编程):
编程中所有的程序是由事件决定–可以是由用户操作(键盘,鼠标),也可以是由其他程序和流的到达或者操作系统事件(如网络数据包到达)来触发执行.
面向事件编程可以也被定义为,写一个计算机程序,在其中的代码(通常程序的功能的头部)被明确分配应用程序的主回路,其代码本身由两部分组成方法:事件和事件处理的代码。
面向事件的编程通常被应用在三种情况下:
1.创建用户界面的控制(包括图形)
2.创建一个基于服务器的应用程序
3.游戏编程时多个对象的管理
我们系统管理时,这种应用在服务器的应用程序中使用面向事件的编程很多,比如用于服务器应用解决10,000个并发连接(所谓C10k问题)
AnyEvent是一个性能非常好的基于事件驱动的程序,有人使用它来解决C10k的问题,象平时我们写的程序,都是基于过程.我们都是先做完事件1->然后做事件2->然后做事件3.这种方式.
但基于事件就完全不一样了,在主流程中你基本只有一个主体框架,程序的动作触发都是由事件来驱动.比如我们使用的窗口程序.点最大化最小化,都是基于事件,当接收到了最大化的事件做最大化事件那部分的程序开始运行.不在从头到尾部来执行.所以我们读基于事件的程序,最好是画成思维导图来帮助我们理解.
基于事件的程序常用到的最大好处是用来做异步,例如,我们要下载100个文件,下载完后对这些文件进行处理.可能给每个下载和处理的过程写成事件,这些事件可以同步运行(关键在于网络连接和进行文件的读写IO时要等待,事件是给这些等待复用起来).
不知大家了解Perl中的select这个功能不,就是等到句柄可以读或者写的时候,做不同的读或者写的操作.事件循环也是一样.
在整个AnyEvent入门中,我们只要关注二个点就行,WATCHERS(监控者)和条件变量.
WATCHERS(监控者)
在select中,有个角色叫"监控者",就是select函数本身.
在AnyEvent中不但可以监控IO还可以监控别的一些事件.来做不同的处理.我们可以看成这是不断的盯着某件事情的人
有如下几个基本的内置的可以用来盯着的事情("监控者").
TIMER:监控时间,到了一定的条件,然后对不同的时间做不同的事件
I/O:这个是监控到IO是否可以读写,然后做相应的事件
IDLE:空闲时做什么事件
SIGNAL:监控观查到不同的信息,调用相应的事件
CHILDPROCESS:对子程序的状态来调用相应的处理事件
TIMERWATCHERS
基本语法
AnyEvent->timer( after=>$seconds,#多久之后做相应的操作. interval=>$seconds,#在上面条件生效后,每格多久进行一次callback. cb=>$cb,#cb是callback的简写,所以知道了吧,只要到了前面的条件,就会运行cb=>指向的函数. );
使用实例:
下面的例子是,5秒后,每2秒进行一次callback中的事件,直到$w这个注册的事件被undef为止(也就是$count>10次).这个中的undef$w是取消掉这种watcher的方法.
#!/usr/bin/perl usestrict; useAnyEvent; my$cv=AnyEvent->condvar; my$count=0; my$w;$w=AnyEvent->timer( after=>5, interval=>2, cb=>sub{ $count++; warn"这是第$count次调用"; if($count>=10){ undef$w; } } ); $cv->recv;
I/OWATCHERS
基本语法
my$fh=....;#打开一个句柄 my$io;$io=AnyEvent->io( fh=>$fh,#上面打开的句柄,也可以是标准输入和输出 poll=>"w",#这个地方可以选择r和w来表示读和写的IO事件 cb=>sub{ syswrite($fh,"写入的内容"); undef$io; } );
使用实例:
下面的例子,是使用io监控到可以读,就调用cb的函数,直接读文件test.txt,每次一个字节,直到读完这个文件就通过undef消掉这个事件.
#!/usr/bin/perl usestrict; useAnyEvent; my$cv=AnyEvent->condvar; openmy$fh,"<test.txt"ordie"不能打开文件句柄$!"; 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"读出错:$!"; } }); $cv->recv;
IDLEWATCHERS
基本语法
my$w=AnyEvent->idle(cb=>sub{...});
使用实例:
下面的例子,当整个程序中,没有其它事件在运行时,就会运行idle.它就是当其它事件都在等待和空着的时候,所调用的.
#!/usr/bin/perl usestrict; useAnyEvent; my$cv=AnyEvent->condvar; my$t;$t=AnyEvent->timer( after=>1, interval=>1, cb=>sub{printtime()."\n"} ); my$w;$w=AnyEvent->idle( cb=>sub{ warn"idle"; #undef$w; } ); $cv->recv;
SIGNALWATCHERS
基本语法如下,就是当接收到POSIXsignal的时候,运行callback中的事件.
my$w=AnyEvent->signal(signal=>"TERM",cb=>sub{...});
CHILDPROCRSSWATCHERS
基本语法如下
#childprocessexit my$w=AnyEvent->child(pid=>$pid,cb=>sub{ my($pid,$status)=@_; ... });
条件变量(多个条件时)
这个是AnyEvent学习上面几种事件监控后必须要了解的.大家都见到上面有AnyEvent->condvar;和$cv->recv这二个,condvar是conditionvariable的简写.是指当什么样的条件成立时的变量
其实就是条件,当达到什么条件时退出事件循环.所以AnyEvent中没有传统事件中的loop函数.所以使用条件变量就相当于让事件这个转起来.
基本的$cv->recv是和$cv->send成对出现的,当事件调用send时,就一定要有recv收到这个调用,才会退出事件.
下面的$cv->begin和$cv->end也基本是这个意思.send是单个条件.begin和end是多个条件成立时退出,换个语来讲,就是这些事件都成对的完成后,才退出事件.
#!/usr/bin/perl usestrict; useAnyEvent; my$cv=AnyEvent->condvar(cb=>sub{ warn"调用结束"; }); formy$i(1..10){ $cv->begin; my$w;$w=AnyEvent->timer(after=>$i,cb=>sub{ warn"finishedtimer$i"; undef$w; $cv->end; }); } $cv->recv;
默认的condvar会对事件建一个条件为假的变量,所以直接有send和beginsend之类才会变成真,然后退出事件循环.可以给这个地方看成一个信号量来理解就好了.y
如果条件不成立,在AnyEvent中事件会一直loop.所以上面的例子中没有send.
有关AnyEvent其它,大家入门后可以玩玩象AnyEvent::HTTP,twiggy之类.看看这些应用和项目.
另外,在AnyEvent中我们常常使用EV.他是一个C的libev的Perl接口,有非常高的性能.看完上面,在看看下面EV的使用,非常容易吧,基本不变.只是没出现条件变量,
使用的传统的EV::loop;来使这个运行起来.
useEV; #TIMERS my$w=EV::timer2,0,sub{ warn"iscalledafter2s"; }; my$w=EV::timer2,2,sub{ warn"iscalledroughlyevery2s(repeat=2)"; }; undef$w;#destroyeventwatcheragain my$w=EV::periodic0,60,0,sub{ warn"iscalledeveryminute,ontheminute,exactly"; }; #IO my$w=EV::io*STDIN,EV::READ,sub{ my($w,$revents)=@_;#allcallbacksreceivethewatcherandeventmask warn"stdinisreadable,youentered:",<STDIN>; }; #SIGNALS my$w=EV::signal'QUIT',sub{ warn"sigquitreceived\n"; }; #CHILD/PIDSTATUSCHANGES my$w=EV::child666,0,sub{ my($w,$revents)=@_; my$status=$w->rstatus; }; #STATCHANGES my$w=EV::stat"/etc/passwd",10,sub{ my($w,$revents)=@_; warn$w->path,"haschangedsomehow.\n"; }; #MAINLOOP EV::loop;#loopuntilEV::unloopiscalledorallwatchersstop EV::loopEV::LOOP_ONESHOT;#blockuntilatleastoneeventcouldbehandled EV::loopEV::LOOP_NONBLOCK;#trytohandlesameevents,butdonotblock
注:本文中大部分内容来自日本的@lestrrat