2013-10-21 33 views
5

Trong ứng dụng Mojolicious, tôi đang cố chuyển đổi tệp ODT thành HTML khi liên kết được nhấp. Tôi chuyển đổi các tập tin bằng cách sử dụng "soffice", một lệnh shell. Chuyển đổi các tập tin mất một thời gian. Tôi gửi thông báo trạng thái cho người dùng để thông báo cho anh ta về tiến trình. Tôi gửi các thông báo cập nhật trạng thái đó bằng cách ghi vào đối tượng Mojo :: Log. Sau đó, tôi đăng ký đối tượng đăng nhập này trong một tuyến đường EventSource.Sử dụng AnyEvent run_cmd trong Mojolicious, tôi tiếp tục nhận được lỗi này: "AnyEvent :: CondVar: chờ đợi đệ quy đang chờ"

Sau đó, tôi lặp qua các tệp và sử dụng AnyEvent :: Util run_cmd để thực thi chương trình "soffice" bên ngoài.

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; 
} 

Rất nhiều được sao chép và dán từ hướng dẫn chính của AnyEvent. Nếu chỉ có vài tệp để chuyển đổi (khoảng 2 hoặc 3), thì mọi thứ đều ổn. Các thông điệp trạng thái được gửi thông qua kết nối EventSource xuất hiện trên trình duyệt của máy khách. Sau đó, sau khi tất cả các tệp đã được chuyển đổi, trang web được hiển thị.

Nếu có nhiều tệp được xử lý, một vài tệp sẽ được chuyển đổi thì thông báo lỗi trong tiêu đề chủ đề sẽ xuất hiện.

Định tuyến cho các tuyến đường có chứa các mã trên là thế này:

my $initdocs = $r->under->to('docroute#initdocs'); 
$initdocs->get('/showdocs')->to('docroute#showdocs'); 

Đoạn mã trên là trong "initdocs" lộ trình.

Mọi trợ giúp đều được đánh giá cao. Cảm ơn trước.

+2

chỉ là fyi, trong tương lai, thêm thẻ [tag: perl] sẽ giúp nhiều người xem câu hỏi thú vị của bạn hơn. –

Trả lời

3

Creating single Thread Server with AnyEvent

AnyEvent recursive Blocking..

Nếu bạn đang sử dụng AnyEvent, bạn thường phải đối phó với CondVars. Có hai điều bạn có thể làm với một CondVar: Hoặc bạn đăng ký một cuộc gọi lại sẽ được gọi khi CondVar được kích hoạt, hoặc bạn gọi recv và nó sẽ chặn cho đến khi CondVar được kích hoạt. Trong một lộ trình của Bộ điều khiển Mojo ::, bạn có thể muốn chặn cho đến khi bạn nhận được tất cả dữ liệu bạn muốn hiển thị cho người dùng của mình.

Lấy sau (made-up) ví dụ trong đó sử dụng một CondVar:

chưa được kiểm tra:

get '/' => sub { 
    ... 
    my $cv = AnyEvent->condvar; 
    my $timer = AnyEvent->timer(after => 1, cb => sub { $cv->send(1) }); 
    my $result = $cv->recv; 
    ... 
}; 

Bạn sẽ nhận được một lỗi runtime nêu rõ "AnyEvent :: CondVar: chặn đệ quy chờ phát hiện". Có lẽ điều này là bởi vì Morbo cũng sử dụng một CondVar như exit_guard, để chạy vô hạn lâu (chặn trên CondVar là một cách dễ dàng để chạy vòng lặp chính).

Tiếp cận của tôi sẽ được sử dụng một eventloop cụ thể, chẳng hạn như EV, và gọi EV-> vòng lặp thay vì chặn trên CondVar:

EV->loop 
4

Tôi nghĩ rằng những gì bạn đang cố gắng làm là để gọi soffice (chặn) quá trình mà không chặn phần còn lại của chủ đề máy chủ. Tôi không có chuyên gia về AE nhưng tôi không nghĩ rằng đó là những gì run_cmd nào. Nó là gần hơn với những gì fork_call hiện mặc dù. Có lẽ những gì bạn muốn làm là một cái gì đó như thế này:

#!/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; 

Trong ví dụ của tôi, tôi chỉ thực hiện cuộc gọi chặn đơn giản, nhưng bạn có thể dễ dàng gọi ra để soffice.

Bây giờ, vì bạn nói rằng bạn có thể phải chuyển đổi nhiều tệp khác nhau trước khi quay lại ứng dụng, bạn có thể muốn sử dụng số Mojo::IOLoop::Delay tuyệt vời để quản lý quy trình.

#!/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; 

Một lần nữa, tôi chỉ ghi lại đầu ra và gửi đến cuộc gọi kết xuất, nhưng lưu ý rằng nó chờ tất cả công việc cần hoàn thành trước khi trở về. Một cái gì đó gần gũi hơn với trường hợp sử dụng thực sự của bạn có thể là:

#!/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; 

này chạy soffice trên mỗi tên tập tin thông qua như là một param để tuyến (/?file=myfile&file=otherfile). Sau đó, các stdout, stderr và mã trả về (cũng nên được, tôi đã không chạy này rõ ràng) rendered như json cho khách hàng (bạn có thể dễ dàng đăng nhập nó).

+0

Tôi chỉ muốn đề cập đến (kể từ khi bài đăng này xuất hiện) rằng câu hỏi này cuối cùng đã truyền cảm hứng cho tôi để tạo mô-đun này trên cpan: https://metacpan.org/pod/Mojo::IOLoop::ForkCall –

Các vấn đề liên quan