2

つまり、イベント ループが現在キューにあるイベントを処理するようにします。VBA では、呼び出しの名前はDoEventsです。

これをPerlでやりたい。できれば AnyEvent でこれを行うことをお勧めします。スクリプトを使用して記述したためです。しかし、ドキュメントにはそのような機能は見つかりませんでした。

ドキュメントにはイベントループを呼び出すと書かれているため、単純にcondvarで実装しようとしましたrecvが、失敗しました。サンプルコードは次のとおりです。

use strict;
use warnings;

use AnyEvent;
use AnyEvent::Strict;

sub _long_task;

my $t1 = AE::timer 0, 3, sub{print"task 1, with interval 3 seconds\n";};
my $t2 = AE::timer 0, 7, sub{print"task 2, with interval 7 seconds\n";};
my $t3 = AE::timer 0, 11, sub{print"task 3, with interval 11 seconds\n";};
my $t_long = AE::timer 0, 0, \&_long_task;

sub DoEvents()
{
    my $cv = AE::cv;
    my $t = AE::timer 0, 0, sub { $cv->send; };
    $cv->recv;
}

sub _long_task {
    print"long task: ENTERING\n";
    for(1..5) {
        print"long task: doing some work for 2 seconds\n";
        sleep 2;
        print"long task: calling DoEvents\n";
        DoEvents();
    }
    print"long task: EXITING, resheduling after 10 seconds\n";
    $t_long = AE::timer 10, 0, \&_long_task;
}

AE::cv->recv;

出力は次のとおりです。

task 1, with interval 3 seconds
task 2, with interval 7 seconds
task 3, with interval 11 seconds
long task: ENTERING
long task: doing some work for 2 seconds
long task: calling DoEvents
AnyEvent::CondVar: recursive blocking wait attempted at C:\Users\andreyi\Desktop\try_event_loop.pl line 18.

更新: AnyEvent.pm に次の行があります。

  $WAITING
     and Carp::croak "AnyEvent::CondVar: recursive blocking wait attempted";

それらにコメントすると、DoEvents() が機能します。

ただし、この CPAN モジュールの変更を伴わない解決策を用意した方がよいでしょう。

4

1 に答える 1

1

すべての問題には、少なくとも 1 つの単純な解決策があります (場合によっては、汚いハックです)。

私の場合、これはうまくいくようです。プロダクションコードに追加しました。

BEGIN { our $orig_Carp_croak = \&Carp::croak; }
sub DoEvents()
{
    my $cv = AE::cv;
    my $t = AE::timer 0, 0, $cv;
    no warnings 'redefine';
    local *Carp::croak = sub{
        (caller 1)[3] eq 'AnyEvent::CondVar::Base::recv'
            && $_[0] =~ /recursive blocking wait attempted/
            && return;
        goto \&{our $orig_Carp_croak};
    };
    $cv->recv;
}

更新: DoEvents を呼び出すすべてのコールバックについて、再入力されないようにする必要があります。このような:

our $entered_callbacks = {};
# ...
sub some_callback {
    $entered_callbacks->{some_callback} && return;
    local $entered_callbacks->{some_callback} = 1;
    ...;
}

ループ EV および AnyEvent::Loop には、コールバックが呼び出される前ではなく、返されたときにのみキューから削除されるという欠陥があります。これにより、イベント ループが再突入に対して安全ではなくなります。

于 2015-08-26T11:05:11.077 に答える