perl - 在 Mojolicious 中使用 AnyEvent run_cmd,我不断收到此错误 : "AnyEvent::CondVar: recursive blocking wait attempted"

标签 perl asynchronous mojolicious anyevent

在 Mojolicious 应用程序中,我试图在单击链接时将 ODT 文件转换为 HTML。我使用 shell 命令“soffice”转换文件。转换文件需要一些时间。我向用户发送状态消息以通知他进度。我通过写入 Mojo::Log 对象来发送这些状态更新消息。然后,我在 EventSource 路由中订阅此日志对象。

然后我遍历文件并使用 AnyEvent::Util run_cmd 执行外部“soffice”程序。

for my $file (@{ $filelist }) {
   my $output_dir = './output_dir';
   my $cmd = "soffice --headless --convert-to html --outdir '$output_dir' '$file'";
   my $cv = AnyEvent->condvar;
   my $w;
   $w = run_cmd($cmd, 
                '>'  => sub { my $out = shift;
                              &WriteToLog({ status => "cmd output '$out'..." });
                              undef $w;
                              $cv->send;
                 },

                '2>' => sub { my $err = shift;
                              &WriteToLog({ status => "ERROR '$err'..." });
                              undef $w;
                              $cv->send;
                 }
            );

   $cv->recv;
}

几乎从主要的 AnyEvent 教程中复制和粘贴。如果要转换的文件很少(大约 2 或 3 个),则一切顺利。通过 EventSource 连接发送的状态消息显示在客户端浏览器上。然后在所有文件都被转换后,呈现网页。

如果要处理更多文件,则会转换一些文件,然后出现线程标题中的错误消息。

包含上述代码的路由的路由是这样的:
my $initdocs = $r->under->to('docroute#initdocs');
$initdocs->get('/showdocs')->to('docroute#showdocs');

上面的代码在“initdocs”路径中。

任何帮助表示赞赏。提前致谢。

最佳答案

我认为您要做的是在不阻塞服务器线程的其余部分的情况下调用 soffice(阻塞)进程。我不是 AE 方面的专家,但我认为这不是 run_cmd做。更接近什么fork_call确实如此。也许你想要做的更像是这样的:

#!/usr/bin/env perl

use Mojolicious::Lite;
use EV;
use AnyEvent::Util 'fork_call';

any '/' => sub {
  my $c = shift;
  $c->render_later;
  fork_call { `sleep 5 && echo 'hi'` } sub {
    my $data = shift;
    $c->render( text => $data );
  };
};

app->start;

在我的示例中,我只是进行了一个简单的阻塞调用,但您可以轻松调用 soffice .

现在既然你说在返回客户端之前你可能需要转换几个不同的文件,你可能想使用优秀的 Mojo::IOLoop::Delay来管理流程。
#!/usr/bin/env perl

use Mojolicious::Lite;
use EV;
use AnyEvent::Util 'fork_call';

my @jobs = (
  q{sleep 5 && echo 'hi'},
  q{sleep 5 && echo 'bye'},
);

any '/' => sub {
  my $c = shift;
  $c->render_later;
  my $delay = Mojo::IOLoop->delay;
  $delay->on( finish => sub { 
    shift; $c->render(text => join '', @_ );
  });
  fork_call { `$_` } $delay->begin(0) for @jobs;
};

app->start;

再一次,我只是捕获输出并将其发送到渲染调用,但请注意它在返回之前等待所有作业完成。更接近您的真实用例的可能是:
#!/usr/bin/env perl

use Mojolicious::Lite;
use EV;
use AnyEvent::Util 'fork_call';
use Capture::Tiny 'capture';

any '/' => sub {
  my $c = shift;
  my $files = $c->every_param('file');
  $c->render_later;
  my $delay = Mojo::IOLoop->delay;
  $delay->on( finish => sub { 
    shift; $c->render( json => \@_ );
  });
  my $output_dir = './output_dir';
  for my $file (@$files) {
    my $cmd = "soffice --headless --convert-to html --outdir '$output_dir' '$file'";
    fork_call { [ capture { system $cmd } ] } $delay->begin(0);
  }
};

app->start;

这对作为参数传递给路由的每个文件名运行 soffice ( /?file=myfile&file=otherfile )。然后 stdout、stderr 和返回代码(应该是,我显然没有运行过这个)作为 json 呈现给客户端(您可以轻松地记录它)。

关于perl - 在 Mojolicious 中使用 AnyEvent run_cmd,我不断收到此错误 : "AnyEvent::CondVar: recursive blocking wait attempted",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19505798/

相关文章:

Perl:以编程方式删除 PostgreSQL 表索引,然后使用 DBD::Pg 在 COPY 之后重新创建

javascript - 将 Observables 链接到 ajax 调用的队列列表

javascript - 处理前端的异步代码

perl - 从我的脚本下载文件时,前 8 个字节总是错误的

perl - Mojolicious、重定向、 session 并尝试创建身份验证系统

perl - Atom 编辑器和嵌入式 perl : syntax highlighting

perl - 测试::简单检查文件是否存在

perl - 有没有一种简单的方法可以将文本文件分块成大括号平衡的部分?

perl - 如何模拟 cron 作业

ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成?