也就是说,让事件循环处理当前在队列中的事件。在VBA中,呼叫名为DoEvents
。
我想在Perl中这样做。最好在AnyEvent中执行此操作,因为我有一个用它编写的脚本。但我在文档中找不到任何类似的功能。
我天真地尝试用condvar实现它,因为文档说recv
调用事件循环,但它失败了,这里是示例代码:
use strict;
use warnings;
use AnyEvent;
use AnyEvent::Strict;
sub _long_task;
my $t1 = AE::timer 0, 3, sub{print"task 1, with interval 3 seconds\n";};
my $t2 = AE::timer 0, 7, sub{print"task 2, with interval 7 seconds\n";};
my $t3 = AE::timer 0, 11, sub{print"task 3, with interval 11 seconds\n";};
my $t_long = AE::timer 0, 0, \&_long_task;
sub DoEvents()
{
my $cv = AE::cv;
my $t = AE::timer 0, 0, sub { $cv->send; };
$cv->recv;
}
sub _long_task {
print"long task: ENTERING\n";
for(1..5) {
print"long task: doing some work for 2 seconds\n";
sleep 2;
print"long task: calling DoEvents\n";
DoEvents();
}
print"long task: EXITING, resheduling after 10 seconds\n";
$t_long = AE::timer 10, 0, \&_long_task;
}
AE::cv->recv;
输出结果为:
task 1, with interval 3 seconds
task 2, with interval 7 seconds
task 3, with interval 11 seconds
long task: ENTERING
long task: doing some work for 2 seconds
long task: calling DoEvents
AnyEvent::CondVar: recursive blocking wait attempted at C:\Users\andreyi\Desktop\try_event_loop.pl line 18.
更新 AnyEvent.pm中有一些行:
$WAITING
and Carp::croak "AnyEvent::CondVar: recursive blocking wait attempted";
如果你评论它们,那么DoEvents()可以正常工作。
但是,最好使用不涉及此CPAN模块的mofidication的解决方案。
答案 0 :(得分:1)
每个问题至少有一个简单的解决方案(有时它是一个肮脏的黑客)。
就我而言,这个似乎有效。我把它添加到生产代码中。
BEGIN { our $orig_Carp_croak = \&Carp::croak; }
sub DoEvents()
{
my $cv = AE::cv;
my $t = AE::timer 0, 0, $cv;
no warnings 'redefine';
local *Carp::croak = sub{
(caller 1)[3] eq 'AnyEvent::CondVar::Base::recv'
&& $_[0] =~ /recursive blocking wait attempted/
&& return;
goto \&{our $orig_Carp_croak};
};
$cv->recv;
}
<强>更新强> 对于调用DoEvents的所有回调,您需要确保它们不会被重新输入。像这样:
our $entered_callbacks = {};
# ...
sub some_callback {
$entered_callbacks->{some_callback} && return;
local $entered_callbacks->{some_callback} = 1;
...;
}
循环EV和AnyEvent :: Loop有一个缺陷,即只有在返回时才会从队列中删除回调,而不是在调用之前。它使事件循环对于重新进入是不安全的。