Perl POE :: Wheel :: FollowTail работает в потоке без изменения глобальных переменных - PullRequest
1 голос
/ 22 июня 2011

В этой программе POE :: Wheel :: FollowTail хорошо работает для отслеживания хвоста файла, он также выполняется в отдельном потоке для простого отслеживания хода выполнения задания компиляции.
Внутри обработчика InputEvent естьгрубое регулярное выражение для извлечения результатов компиляции, и там все работает нормально, но я не могу получить какие-либо значения результатов, которые будут доступны за пределами этого подпрограммы.Даже если я помещу переменные результата в глобальную область видимости, они не изменятся.

Программа состоит из одного процесса, выполняющего задание компиляции, другого, просматривающего журнал, и ожидания основного цикла.

Globalscope:

my $Pass = 0;
my $Done = 0;

Затем запустить мониторинг:

threads->create(\&StartWatcher);

Где подпрограмма файла watch-log выглядит следующим образом:

sub StartWatcher
{
my $logfile = "filename.log";

# Create the logfile watcher
POE::Session->create
    (
    inline_states => 
        {
        _start => sub 
            {
            $_[HEAP]{tailor} = POE::Wheel::FollowTail->new( Filename => $logfile, InputEvent => "got_log_line", );
            },
        got_log_line => sub 
            {
            $Pass += () = $_[ARG0] =~ /^\d+.*vcproj \- 0 error\(s\), \d+ warning\(s\)/g;
            $Done += () = $_[ARG0] =~ /^\d+.*vcproj \- \d+ error\(s\), \d+ warning\(s\)/g;
            print "POE InputEvent Pass: $Pass, Done: $Done\n"; # Debug output
            },
        }
    );

POE::Kernel->run();
}

Файл журнала $записывается заданием компиляции Visual Studio, запущенным с использованием Win32 :: Process :: Create, и основное выполнение Perl находится в этом цикле, ожидая завершения работы компилятора и создавая вывод состояния каждую секунду.

    while('true')
    {
    $ProcessObj->Wait(100);  # milliseconds wait
    $ProcessObj->GetExitCode($exitcode);
    if ( $exitcode == STILL_ACTIVE ) 
        {
        "Compiling... [$Done/$Count]  Pass: $Pass  Failed: $failed" 
            if($RunCounter++ % 10 == 0);
        next;
        }
    last;
    }

Полученная продукция похожа на это:

POE InputEvent Pass: 1, Done: 1
Compiling... [0/91]  Pass: 0  Failed: 0                           

т.е.в обработчике InputEvent got_log_line две глобальные переменные были увеличены, но в основном цикле Perl они по-прежнему равны нулю.Я понимаю, что могу сделать вывод на печать из обработчика InputEvent, но почему он не изменяет глобальные переменные?

Что идет не так?

Ответы [ 2 ]

5 голосов
/ 22 июня 2011

Потоки в perl не работают так же, как и в других языках, пространство программы не разделяется. При создании потока текущий поток копируется в новый, который отделен от родительского (у каждого потока есть свой экземпляр интерпретации perl). Если вы хотите общаться между потоками, посмотрите на потоки :: shared, Thread :: Queue и Thread :: Semaphore.

0 голосов
/ 22 июня 2011

Следуя предложению anydot выше, вот ответ:

Перед запуском потока создайте общую очередь

use threads;
use Thread::Queue;
use threads::shared;
my $queue:shared = Thread::Queue->new();

Во входном событии создайте некоторые общие данные для постановки в очередь

        my %data:shared = ();

        $data{PASS_VCPRJ_COUNT} = () = $_[ARG0] =~ /^\d+.*vcproj.*0 error.*\d+ warning/g;
        $data{DONE_VCPRJ_COUNT} = () = $_[ARG0] =~ /^\d+.*vcproj.*d+ error.*\d+ warning/g;
        $queue->enqueue(\%data) if($data{DONE_VCPRJ_COUNT} ne 0 || $data{PASS_VCPRJ_COUNT} ne 0);

Затем в коде обновления экрана удалите его, здесь неблокирование

if (defined(my $item = $queue->dequeue_nb()))
    {
    foreach my $key(%$item)
    {print "$key       $item->{$key}\n";}
    }

Есть другие способы, я уверен, но это работает для меня.Большое спасибо.

...