我们已经有一个使用 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 部分明确表示:
上面的
getData()
违反了这一点。现在我明白了 AnyEvent 文档那部分的原因:-) 最佳答案
您可以通过确保 Mojolicious 和 AnyEvent 不使用相同的主循环来避免此问题,或者通过设置 env var MOJO_REACTOR=Mojo::Reactor::Poll
,或者在其他任何事情之前使用 AnyEvent::Loop 以便 AnyEvent 使用其纯 perl 循环(首选,因为它没有被使用作为主循环)。不幸的是,没有办法让它使用单独的实例化循环,例如 Mojo::UserAgent blocking requests 函数。
请注意,通常您希望多个循环使用者共享主循环;这是一个奇怪的情况,您希望内部消费者阻止。
更“异步”的长期解决方案可能是简单地允许操作共享主循环,因为 Mojolicious 是为非阻塞响应操作而设计的。您可以首先确保两者确实共享 EV 主循环(例如通过设置 MOJO_REACTOR=Mojo::Reactor::EV
),然后更改您的函数,或创建函数的新版本,以返回函数将异步填充的 promise结果,并将依赖于该结果的任何进一步功能链接到该 promise 之外。当然,您的函数比您在此处使用的单个计时器更复杂,但它仍然值得考虑——它允许应用程序在等待 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 });
};
关于perl - 避免 Mojolicious 异步行为?避免 "AnyEvent::CondVar: recursive blocking wait attempted",我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56447536/