我们已经有一个使用 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/

10-11 15:40