0

次のようなPerlGTK+アプリケーションを作成したいと思います。

0.1)ボタンAを押します
0.2)Aを無効にします
0.3)スレッド1と2を
開始します0.4)スレッド3を開始します

スレッド3は次のことを行います。

3.1)スレッド1に参加
3.2)スレッド2に参加
3.3)Aを有効にする

スレッド3が完了すると、ボタンAが再び有効になります。

現在、この種のアプローチは、ネイティブGUIライブラリおよび/またはGTK +、KDEを使用するWin32、LinuxのC /C++で完全に有効です。GTK +とPerlの問題は、スレッド内でボタン変数を共有できないことです(たとえば、ポイント3.3はスレッド3では実行できません)。

問題は、threads :: sharedが基本型でのみ機能し、。のような参照では機能しないことGtk2::Buttonです。

(ドキュメントに示されているように)オブジェクトを再試行しましblessたが、エラーが発生しました:Gtk2::Button

my $thread_button = shared_clone(Gtk2::Button->new('_Threads'));
bless $thread_button => 'Gtk2::Button';
$hbox->pack_start($thread_button, FALSE, FALSE, 0);
my ($jobA, $jobB);
$thread_button->signal_connect( clicked => sub {
        $thread_button->set_sensitive(0);
        if (defined($jobA)) {
            $jobA->join();
        }
        if (defined($jobB)) {
            $jobB->join();
        }
        # spawn jobs
        $jobA = threads->create(\&async_func, 10);
        $jobB = threads->create(\&async_func, 10);
        threads->create(sub { 
            $jobA->join();
            $jobB->join();
            bless $thread_button => 'Gtk2::Button';
            $thread_button->set_sensitive(1);
            });
    });

私のコードは大丈夫ですか?
GUIを実行すると、 [スレッド]ボタンが表示されず、次のエラーが報告されるため、質問しています。

Gtk-CRITICAL **:gtk_box_pack:アサーション `GTK_IS_WIDGET(child)'がvbox.plの48行目で失敗しました。(pack_startを使用する場合)
GLib-GObject-警告**:vbox.plの67行目の無効な(NULL)ポインタインスタンス。
GLib-GObject-CRITICAL **:g_signal_connect_closure:アサーション `G_TYPE_CHECK_INSTANCE(instance)'がvbox.plの67行目で失敗しました(signal_connectが機能しません)

どうやらこれは複雑なオブジェクトでは機能しません。
私は別の修正を試みました。メイン(GTK)スレッドで呼び出されたコールバック関数内で実行中のスレッドをポーリングします。

my $thread_button = Gtk2::Button->new('_Threads');
$hbox->pack_start($thread_button, FALSE, FALSE, 0);
my ($jobA, $jobB);
$thread_button->signal_connect( clicked => sub {
        $thread_button->set_sensitive(0);
        # spawn jobs
        $jobA = threads->create(\&async_func, 10);
        $jobB = threads->create(\&async_func, 10);
        Glib::Timeout->add(3000, sub { 
                print "TIMER\n";
                if (defined($jobA)) {
                    if (! $jobA->is_running()) {
                        print "jobA is not running!\n";
                        $jobA->join();
                        undef $jobA;
                    }
                }
                if (defined($jobB)) {
                    if (! $jobB->is_running()) {
                        print "jobB is not running!\n";
                        #$jobB->join();
                        undef $jobB;
                    }
                }
                if (!defined($jobA) && !defined($jobB)) {
                    print "Both jobs have terminated!\n";
                    $thread_button->set_sensitive(1);
                    return 0;
                }
                return 1;
                });
    });

次の点に注意してください。1) 2番目のスレッドの結合
に コメントする必要があります。

#$jobB->join();

そうしないと、アプレットがクラッシュします。
2)どうやら動作しているようですが、再度有効にしたボタンを2回クリックすると、スレッドの作成によってアプリケーションがクラッシュします。

これは非常に不安定です。PerlはもっとCベースだと思いましたが、この巨大な不安定性はC /C++にはまったくありません。少しがっかりしました。
誰かがもっと提案がありますか?マルチスレッドAPIはPerlではそのように不安定ですか?

最新のアップデート。このコードは機能します:

my $thread_button = Gtk2::Button->new('_Threads');
$hbox->pack_start($thread_button, FALSE, FALSE, 0);
my ($jobA, $jobB);
$thread_button->signal_connect( clicked => sub {
        $thread_button->set_sensitive(0);
        # spawn jobs
        $jobA = threads->create(\&async_func, 10);
        $jobB = threads->create(\&async_func, 10);
        Glib::Timeout->add(100, sub { 
                if (!$jobA->is_running() && !$jobB->is_running()) {
                    print "Both jobs have terminated!\n";
                    $thread_button->set_sensitive(1);
                    return 0;
                }
                return 1;
                });
    });

しかし:
1)スレッドをポーリングする必要があります(最新のCPUにはあまりリソー​​スを消費しませんが、エレガントではありません... OS同期プリミティブのみに依存する必要があります)
2)スレッドに参加できません。そうしないとアプレットがクラッシュします
3)与えられた(2 )ボタンを押すたびに大量のメモリリークが発生します

正直なところ、これを見れば見るほど、適切なアプリ開発のためにPerlに頼ることはできないと確信しています...しかし、プロトタイプの観点からさえ、それはちょっとひどいです。
私は何か間違ったことをしているといいのですが...この場合、誰かが私を助けてくれますか?

乾杯、

4

2 に答える 2

2

threads::sharedドキュメントで説明されているように、共有オブジェクトを再祝福する必要があります。

更新:次のバリエーションを試してください

#!/usr/bin/perl

package Button;

use strict;  use warnings;
# Trivial class because I do not have GTK2

sub new { bless \ my $self => $_[0] }
sub enable     { ${ $_[0] } = 1; return }
sub disable    { ${ $_[0] } = 0; return }
sub is_enabled { ${ $_[0] } ? 1 : 0 }

package main;

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

my $buttonA = shared_clone( Button->new );
my $button_class = ref $buttonA;

$buttonA->disable;

my @thr = map { threads->create(
    sub {
        print "thread $_ started\n";
        sleep rand 3;
        print "thread $_ finished\n";
        return; }
) } (1, 2);

my $thr3 = threads->create( sub {
        $_->join for @_ ;
        bless $buttonA => $button_class;
        $buttonA->enable;
    }, @thr,
);

$thr3->join;

printf "buttonA is %s\n", $buttonA->is_enabled ? 'enabled' : 'disabled';

もう1つの方法は、コールバックを次の宛先に渡すこと$thr3です。

my $buttonA = Button->new;
share($buttonA);
$buttonA->disable;

# start the other threads

my $thr3 = threads->create( sub {
        my $callback = shift;
        $_->join for @_ ;
        $callback->();
    }, sub { $buttonA->enable }, @thr,
);

コードの両方のバージョンが出力を生成します。

スレッド1が開始されました
スレッド2が開始されました
スレッド1が終了しました
スレッド2が終了しました
buttonAが有効になっている
于 2009-08-17T15:28:57.940 に答える