我们有一个已经使用AnyEvent的库。它在内部使用AnyEvent,最后返回一个值(同步-不带回调)。我可以通过Mojolicious使用此库吗?
它的作用类似于:
#!/usr/bin/perl
use strict;
use warnings;
use AnyEvent;
use Mojolicious::Lite;
# To the caller, getData is a synchronous sub that returns a value.
# The fact that it uses AnyEvent is an internal implementation detail of
# getData
sub getData {
my $cv = AnyEvent->condvar;
my $w = AnyEvent->timer (after => 5, cb => sub {
# Perform many async operations, represented here by a single timer,
# calculating a final result that is sent:
$cv->send(42);
});
my $result = $cv->recv;
# postProcess($result);
return $result;
}
get '/' => sub {
my ($c) = @_;
$c->render(text => "Data is: " . getData());
};
app->start;
当我运行morbo app.pl
并尝试同时从两个浏览器选项卡进行get '/'
时,出现此错误:
AnyEvent::CondVar: recursive blocking wait attempted at /bla/bla/app.pl line 16.
我认为这是因为morbo在内部使用EV,因此当它分派处理第一个get '/'
时,$cv->recv
最终会被调用,返回到EV事件循环。 EV现在尝试处理第二个get '/'
,并再次调用$cv->resv
,从而触发错误。
我知道我可以将$cv
中的getData()
重构为异步版本,但实际上在许多地方都调用了真正的“ getData”,并将所有对“ getData”的调用转换为异步代码不可行。
所以我的问题是:在使用getData()
/ Mojolicious时,有什么方法可以可靠地调用上面确切的morbo
?我希望get '/'
进行屏蔽,直到完成。
编辑:AnyEvent的WHAT TO DO IN A MODULE部分明确指出:
上面的请不要在条件变量上调用-> recv,除非您知道-> send方法已被调用过。这是因为它将使整个程序停顿,并且使用事件的全部目的是保持交互性。
getData()
违反了该规定。现在,我了解了AnyEvent文档中该部分的原因:-)
答案 0 :(得分:3)
通过设置env var MOJO_REACTOR=Mojo::Reactor::Poll
或先使用AnyEvent::Loop来确保Mojolicious和AnyEvent不使用同一主循环,可以避免此问题,以便AnyEvent使用其纯净的perl循环(首选,因为它未被用作主循环)。不幸的是,没有其他方法可以使它使用单独的实例化循环,例如Mojo::UserAgent blocking requests的功能。
请注意,通常,您希望多个循环使用者共享主循环。这是一种奇怪的情况,您想要内部消费者阻止。
一个更“异步”的长期解决方案可能是简单地允许操作共享主循环,因为Mojolicious是为非阻塞响应操作而设计的。为此,您可以先确保两者实际上共享EV主循环(例如通过设置MOJO_REACTOR=Mojo::Reactor::EV
),然后更改函数或创建函数的新版本以返回{{3} },该函数将异步地填充结果,并将依赖该结果的任何其他功能链接到该诺言中。当然,您的功能比您在此处使用的单个计时器更复杂,但是仍然值得考虑-它将允许应用程序在等待AnyEvent操作的结果时继续满足其他请求。
sub getData_p {
my $p = Mojo::Promise->new;
my $w; # keep a strong reference to $w for AnyEvent's reasons
$w = AnyEvent->timer(after => 5, cb => sub { $p->done(42); undef $w });
return $p;
}
get '/' => sub {
my ($c) = @_;
my $tx = $c->render_later->tx; # keep strong reference to $tx
getData_p()->then(sub { $c->render(text => "Data is: $_[0]") })
->catch(sub { $c->reply->exception($_[0]); undef $tx });
};