技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 其他 --> Perl之AnyEvent 简单介绍和入门

Perl之AnyEvent 简单介绍和入门

浏览:1621次  出处信息

      AnyEvent 是一个性能非常好的基于事件驱动的程序,象平时我们写的程序,都是基于过程。我们都是先做完事件1, 然后做事件2,然后做事件3 .这种方式。但其实事件就完全不一样了,在主流程中
你基本见不到主体,程序的动作都是由事件来驱动。比如我们使用的窗口程序。点最大化最小化,都是基于事件,当接收到了最大化的事件做最大化事件那部分的程序开始运行。不在从
头到尾部来执行。所以我们读基于事件的程序,最好是画成思维导图来帮助我们理解。

     基于事件的程序常用到的最大好处是用来做异步,例如,我们要下载 100 个文件,下载完后对这些文件进行处理。可能给每个下载和处理的过程写成事件,这些事件可以同步运行。不知大家了解 Perl 中的 select 不,就是等到句柄可以读 or 写的时候,做不同的读 or 写的操作。事件循环也是一样。

在整个 AnyEvent 入门中,我们只要关注二个点就行, WATCHERS(监控者) 和 条件变量。

WATCHERS(监控者)

在 select 中,有个角色叫"监控者",就是 select 函数本身,在 AnyEvent 中不但可以监控 IO 还可以监控别的一些事件。来做不同的处理。
有如下几个基本的"监控者"。
TIMER : 监控时间,到了一定的条件,然后对不同的时间做不同的事件
I/O: 这个是监控到 IO 是否可以读写,然后做相应的事件
IDLE: 空闲时做什么事件
SIGNAL : 监控观查到不同的信息,调用相应的事件
CHILD PROCESS: 对子程序的状态来调用相应的处理事件

TIMER WATCHERS
基本语法

AnyEvent->timer(
    after       => $seconds,    # 多久之后做相应的操作.
    interval   => $seconds,    # 在上面条件生效后,每格多久进行一次 callback.
    cb => $cb,    # cb 是 callback 的简写,所以知道了吧,只要到了前面的条件,就会运行 cb => 指向的函数.
);

使用实例:
下面的例子是,5 秒后,每 2 秒进行一次 callback 中的事件,直到 $w 这个注册的事件被 undef 为止(也就是 $count > 10 次).

#!/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 次调用";
            if ($count >= 10) {
                undef $w; 
            }   
        }   
        );  
$cv->recv;

 

I/O WATCHERS
基本语法

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
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 "读出错: $!";
            }   
        }); 
 
$cv->recv;

 

IDLE WATCHERS

基本语法

my $w = AnyEvent->idle (cb => sub { ... });

使用实例:
下面的例子,当整个程序中,没有其它事件在运行时,就会运行 idle 。它就是当其它事件都在等待和空着的时候,所调用的。

#!/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
基本语法如下,就是当接收到 POSIX signal 的时候,运行 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) = @_;
      ...
   });

 

 

条件变量

这个是AnyEvent 学习上面几种 WATCHERS 后必须要了解的。大家都见到上面有 AnyEvent->condvar; 和 $cv->recv; 吧,这个其实就是条件,当达到什么条件退出事件循环。所以 AnyEvent 中没有传统事件中的 loop 函数。所以使用条件变量来模拟就好了。
基本的 $cv->recv 是和 $cv->send 成对出现的,当事件调用 send 时 recv 收到这个调用,就会退出事件。下面的 $cv->begin 和 $cv->end 也基本是这个意思。send 是单个条件。begin 和 end 是多个条件。

#!/usr/bin/perl
use strict;
use AnyEvent;
 
my $cv = AnyEvent->condvar( cb => sub {
    warn "调用结束";
});
 
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;

默认的 condvar 会对事件建一个条件为假的变量,所以直接有 send 和 begin send 之类才会变成真,然后退出事件循环。可以给这个地方看成一个信号量来理解就好了。y
如果条件不成立,在 AnyEvent 中事件会一直 loop 。所以上面的例子中没有 send 。

 有关 AnyEvent 其它,大家入门后可以玩玩象 AnyEvent::HTTP,twiggy 之类。看看这些应用和项目。

另外,在 AnyEvent 中我们常常使用 EV .他是一个 C 的 libev 的 Perl 接口,有非常高的性能。看完上面,在看看下面 EV 的使用,非常容易吧,基本不变。只是没出现条件变量,
使用的传统的 EV::loop; 来使这个运行起来。

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

建议继续学习:

  1. 使用 AnyEvent 来实现同一个端口跑二种服务    (阅读:2523)
  2. Perl 实现 Flash 的 Socket Policy 服务器    (阅读:2222)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1