Chinaunix首页 | 论坛 | 博客
  • 博客访问: 554065
  • 博文数量: 38
  • 博客积分: 10093
  • 博客等级: 上将
  • 技术积分: 1460
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-24 13:04
文章分类

全部博文(38)

文章存档

2012年(1)

2010年(9)

2009年(3)

2008年(25)

我的朋友

分类:

2008-06-06 08:26:38

起因

Perl-Tk windows下的表现着实让我头疼了很长一段时间,最无法忍受的一点是缺乏稳定的线程支持。比如,无法在一个界面上一边录制新闻,一边完成下一个录制时间的设定,整个程序被阻塞在那儿无法动弹。某些情况下可以使用非阻塞的本地socket来实现进程间的通信,但这么做很费劲,而且使得程序像一个奇怪的网络服务器。POE作为在单进程中实现并行的框架,提供了对Tk的支持,很好地解决了组件间通信的问题。

大致的介绍

# !/usr/bin/perl

use  warnings;
use  strict;

use  Tk;
use  POE;

POE
:: Session -> create(
    inline_states 
=>  {
        _start 
=>   \& ui_start ,
        ev_count 
=>   \& ui_count ,
        ev_clear 
=>   \& ui_clear ,
    }
);

$poe_kernel -> run();
exit   0 ;

sub  ui_start {
    
my  (  $kernel ,   $session ,   $heap  )  =   @_ [ KERNEL ,  SESSION ,  HEAP ];
    
    
$poe_main_window -> Label(  - text  =>   " Counter "  ) -> pack ;
    
$poe_main_window -> Label(  - textvariable  =>   \ $heap -> {count} ) -> pack ;
    
    
$poe_main_window -> Button(
        
- text  =>   " Clear " ,
        
- command  =>   $session -> postback( " ev_clear " ) ,
    )
-> pack ;
    
    
$kernel -> yield( " ev_count " );
}

sub  ui_count {
    
$_ [HEAP] -> {count} ++ ;
        
$_ [KERNEL] -> delay(  " ev_count "   =>   . 5 );

    
#  $_[KERNEL]->yield("ev_count");
}

sub  ui_clear {
    
$_ [HEAP] -> {count}  =   0 ;
}

很容易可以看出来一个不一样的地方,比如说用$poe_kernel代替了一贯的POE::Kernel,但在实际运行中会发现$poe_kernel是对POE:;Kernel的引用,最终起作用的依然是POE::Kernel。好了,除了这一点外,其他还是与传统的POE程序差不多。

首先建立了一个包含了内置_start和两个自定义(ev_countev_clear)事件的session。之后在_start事件中,在已初始化了的$poe_main_window上建立一个内容为“Counter”的标签,内容为变量$_[HEAP]->{count}的标签和一个用来清零的按钮。至于ev_countev_clear事件不过是自增和清零$_[HEAP]->{count}而已,其中ev_count事件会递归回调自身以实现不断自增的过程。

如果一切顺利,运行之后的结果如下图所示:

poe_tk.JPG 

单击Clear按钮,中间的数字将从零开始继续飞速自增。如果想让它自增得慢一点,或者说可以看得清,可以在ev_count事件中delay自身回调一小段时间。需要注意的一点是不要指望用sleep,因为它会让整个POE崩溃。

一些细节

下面来讲一下隐藏在这一切背后的一些小细节。事实上,这些细节危险重重,任何细小的错误哪怕是位置的改变都可能导致程序的失败,而原文中的某些注释也比较晦涩或者是存在误导,需要详细说明一下。

$poe_main_window strict环境中出现,这让我很吃惊。一般情况下,这样的非my变量是无法通过检测的。唯一的解释是use POE在背后干了很多事情,多得连POE::Kernel都增加了叫一个$poe_kernel的引用,当然这是为了保证POE::Kernel不被自动垃圾回收。在这里不得不说的是,use Tk必须出现在use POE之前,否则use POE不会初始化生成$poe_main_window。所以可以这么认为,use POE会通过检测其之前的模块调用来决定之后的框架行为,这点在其他模块的应用中不是很多见,至少在我是第一次看见。

原文中有这么一句话“Widgets we need to work with later, such as the counter display,must be stored somewhere.The heap is a convenient place for them.”意思是说“要在之后用到的组件,比如说显示自增计数的标签,必须存储在某个地方,而堆栈就是一个方便的场所。”这也让我感到很困惑,至少在这个例子中,之后没有显式地用到这个计数显示标签。如果非要说用到,那么就是ev_count事件中的$_[HEAP]->{counter}textvariable的身份影响到其宿主,但事实上这非常隐晦,而且为此保存一个引用似乎也毫无意义。后来的实验证明了我的猜测,在注释掉这个引用之后程序依然可以正常运行,也就是说原文这句话并非针对于本例子或者说毫不相干,其中的“such as”极易引人误入歧途。它真正想说的是,为了可以在某个事件中改变组件的行为,需要一个全局可见的引用,而$_[HEAP]正是恰当的选择,其实用一个全局哈希表就可以得到相同的效果,只是这么多就显得多余了。

最后的一点是有关于POE本身的机制。在ev_count中为了实现不断自增,在其事件处理句柄中有一句自身回调,而在ev_clear中仅仅是将计数清零了而已。一开始我有些担心自增行为会中断,因此在ev_clear中增加了一句触发ev_count事件的命令,但这么做是多余的。首先POE中的每一个事件句柄都是并行的,因此ev_clear不会中断ev_count的行为;第二,作为两个事件之间关联的全局变量$_[HEAP]->{counter},POE为其提供了锁保护,不必担心其中一个事件的行为导致另外一个崩溃。

几句话

因为不是正儿八经地写论文,所以遣词造句就显得随性了一些。倒不是说我可以不认真了,相反地,对于一些细节倒是表现得更自然。

这个例子非常简单,简单得几乎没有实用价值,但是说明了问题。我认为一个好的tutorial就应该这样,简单而能说明问题。简单就是说容易让人看懂,说明问题则是把注意事项讲清楚了,之后全凭各人兴趣和悟性了。POE Cookbook是一本非官方的技术文集,没有出版物,甚至说如果你写了一个好的例子,也可以被收录其中。但是每个例子都很通俗易懂,从最简单的入手,由浅入深,最后形成一个系统。反观国内的一些技术出版物,往往枯燥理论先讲一大堆,吓跑一批人;再举一两个可以堪称作者代表作的复杂例子,又闷掉一批人,结果是弄得是曲高和寡。

阅读(842) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~