前几天看到 cindylinz 发了一个新 CPAN 模块叫 Scalar::Watcher,有朋友问我这个是怎么实现的,在无限循环啊,多线程啊,IO 阻塞啊等情况下,还能被触发么?

于是我去仔细看了一下这个模块的代码。最关键的就是下面这几行:

SvUPGRADE(target, SVt_PVMG);
        sv_magicext(target, handler_cv, PERL_MAGIC_ext, &modified_vtbl, NULL, 0);

这里其实用的是 perlapi 里的 Magic:

  • 第一行,设置监听变量为 SVt_PVMG,即带有 Magic 的标量;

    SvUPGRADE 函数见 perlguts 文档的 “Assigning Magic” 部分。

  • 第二行,设置该变量的 Magic 扩展,即往标量的 Magic 链表上加内容。

    sv_magicext 函数说明见 perlapi 文档的 “SV-Body Allocation” 部分。

Magic 主要有两个作用,一个叫 Hook Method,一个叫 Managed Data。我们都很熟悉的 Moose 框架就是利用的 Magic 的 Managed Data 实现的。而这里,用到的是 Hook Method。

Scalar::Watcher 模块文档较少,虽然好用但是不好懂。我在 CPAN 上发现另一个模块,Variable::Magic 。文档写的很详细。其中的 set 方法就是跟 Scalar::Watcher 类似的作用,大家可以读一读这个模块的文档。

所以可以回答朋友的问题了,在循环之类的地方每次都可以触发没问题。但是如果你在回调函数里面做阻塞操作,那肯定也是堵塞的。