Интерфейс обновления Perl в длинных нитях - PullRequest
3 голосов
/ 25 апреля 2011

У меня есть скрипт Perl, работающий на версии 5.10, сборка 1004 ActiveStates Active Perl на Windows XP, которая создает пользовательский интерфейс, а затем выполняет длинный процесс после нажатия кнопки. Во время этого процесса я хотел бы обновить пользовательский интерфейс (список) со статусом того, что происходит во время выполнения этого потока. Вот урезанная версия кода.

#!/usr/local/bin/perl

use warnings;
use strict;
use Tkx;
use threads;
use threads::shared;

my $outputText = " {a} {b}";

my $mw = Tkx::widget->new(".");
$mw->g_wm_title("MD5 Checker");
$mw->g_wm_minsize(300,200);
my $content = $mw->new_ttk__frame(-padding => "12 12 12 12");
my $btnCompare = $content->new_ttk__button(-text => "Compare", -command => sub{startWork()});
my $lstbxOutput = $content->new_tk__listbox(-listvariable => \$outputText, -height => 5);
my $scollListBox = $content->new_ttk__scrollbar(-orient => 'vertical', -command => [$lstbxOutput, 'yview']);
$lstbxOutput->configure(-yscrollcommand => [$scollListBox, 'set']);

sub startWork()
{
    print "Starting thread \n";
    my $t = threads->create(\&doWork, 1);
    sleep (5);
    print $outputText . "\n";
}

sub doWork()
{
    for (my $a = 0; $a<10; $a++)
    {
        $outputText .= " {$a}";
        print "Counting $a\n";
        sleep(2);
    }
    print "End thread\n";
}

В настоящее время команды печати предназначены для моей отладки, поэтому я знаю, что делают основной и дочерний потоки. Из того, что я прочитал о потоках, мне нужно use threads::shared;, чтобы позволить потокам совместно использовать переменные. В настоящий момент мой список не обновляется ни во время выполнения дочерних потоков, ни после завершения потока. Без создания потока список будет обновляться после того, как основной поток завершит цикл. Чего мне не хватает, чтобы пользовательский интерфейс обновлялся во время выполнения потоков?

Спасибо

Wesley

Ответы [ 2 ]

2 голосов
/ 26 апреля 2011

Одной из проблем является то, что переменная списка должна быть разделена между потоками.Кажется, Tk не устраивает переменная listbox, к которой предоставлен общий доступ напрямую, поэтому я сделал две копии и настроил периодическое обновление статуса, чтобы копировать общую версию в версию без общего доступа.

Однако, используя потоки с Tkxможет быть рискованнымЯ получал segfaults, когда пытался join поток, а не detach его, и я получал segfault с кодом ниже, если я переместил my $t внутрь startWork(). Это обсуждение предполагает, что вам может понадобиться запустить поток перед созданием любых виджетов Tk, чтобы он работал надежно.

Вот код, с которым я закончил:

my $outputTextShared :shared = " {a} {b}";
my $outputText = " {a} {b}";

my $t;
sub startWork()
{
    print "Starting thread \n";
    $t = threads->create(\&doWork, 1);
}

sub updateStatus()
{
    $outputText = $outputTextShared;
}

sub doWork()
{
    threads->detach();
    for (my $a = 0; $a<10; $a++)
    {
        $outputTextShared .= " {$a}";
        print "Counting $a\n";
        sleep(1);
    }
    print "End thread\n";
}

my $update;
$update = sub {
    Tkx::after (1000, $update);
    updateStatus();
};
Tkx::after (1000, $update);

Tkx::MainLoop();
1 голос
/ 16 мая 2011

Потоки хороши тем, что пользовательский интерфейс не блокируется, и вы можете делать такие вещи, как уничтожение дочернего процесса, если он занимает слишком много времени.Эта сила приходит со сложностью, хотя.Если все, что вам нужно, это обновить статус задачи в пользовательском интерфейсе, вы можете сделать это без использования потоков;вам просто нужно сделать это вручную.

$outputText = 'some message';
Tkx::update('idletasks');
...