Perl 支持匿名子程序和闭包。匿名子程序通过没有名称的 sub 声明,返回指向代码的引用。闭包是一个"捕获"词法作用域的子程序,包括创建时存在的变量。
匿名子程序和闭包示例:
sub make_incrementer { my $inc = shift; return sub { $_[0] + $inc }; } my $inc10 = make_incrementer(10); print $inc10->(5); # 输出 15
闭包被广泛用于创建函数工厂、生成器和回调(例如,在 map/grep、事件处理器中)。
重要的一点:如果闭包引用了指向它自身的变量(直接或通过结构体),会产生 循环引用,可能导致内存泄漏。
闭包中封闭的变量何时被释放?对于 my 和 our 有区别吗?
答案: 在闭包内部封闭的 my 变量只要存在对闭包的引用就会保持活跃。它们不会在函数结束时释放,生命周期与闭包相同。而 our 变量则没有这种行为——它们对所有闭包可见,会在程序结束时释放。忘记删除闭包会导致内存泄漏。
问题示例:
my $cref; { my $val = 5; $cref = sub { $val++ }; } # $val 仍然存在,只要 $cref 存在
故事
在服务器应用中,为每个用户创建了一个闭包上下文用于回调。但闭包还引用了用户结构,这导致了循环引用。因此,垃圾回收器即使在注销后也无法清除用户对象——内存泄漏以指数形式增长。
故事
在用于后台事件处理的守护程序应用中,使用了带闭包的计数器变量,但忘记清空它们引用的数组。结果是由于一两个被遗忘的闭包,旧的消息数据不断堆积,守护程序崩溃前不得不手动清理堆。
故事
开发人员尝试在闭包中使用 our 变量,希望获得"闭合"行为——但所有闭包共享一个变量,这导致了在并行执行时的数据竞争。该错误在不同参数的用户同时工作时表现出来(错误的计算)。