zl程序教程

您现在的位置是:首页 >  其他

当前栏目

perlAnyEvent简单介绍和入门知识

入门 简单 介绍 知识
2023-06-13 09:15:46 时间

什么是面向事件的编程(事件驱动的编程):

编程中所有的程序是由事件决定?可以是由用户操作(键盘,鼠标),也可以是由其他程序和流的到达或者操作系统事件(如网络数据包到达)来触发执行.

面向事件编程可以也被定义为,写一个计算机程序,在其中的代码(通常程序的功能的头部)被明确分配应用程序的主回路,其代码本身由两部分组成方法:事件和事件处理的代码。

面向事件的编程通常被应用在三种情况下:

1.创建用户界面的控制(包括图形)
2.创建一个基于服务器的应用程序
3.游戏编程时多个对象的管理

我们系统管理时,这种应用在服务器的应用程序中使用面向事件的编程很多,比如用于服务器应用解决10,000个并发连接(所谓C10k问题)

但基于事件就完全不一样了,在主流程中你基本只有一个主体框架,程序的动作触发都是由事件来驱动.比如我们使用的窗口程序.点最大化最小化,都是基于事件,当接收到了最大化的事件做最大化事件那部分的程序开始运行.不在从头到尾部来执行.所以我们读基于事件的程序,最好是画成思维导图来帮助我们理解.

基于事件的程序常用到的最大好处是用来做异步,例如,我们要下载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