Perl 多线程 GTK+ 小程序

发布于 2024-08-01 21:41:10 字数 4512 浏览 3 评论 0原文

我想编写一个 Perl GTK+ 应用程序,它将:

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 应再次启用。

现在,这种方法在 Win32、Linux 下使用本机 GUI 库和/或 GTK+、KDE ​​下的 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 将不会显示 Thread 按钮并报告以下错误:

Gtk-CRITICAL **: gtk_box_pack: assertion `GTK_IS_WIDGET (child)' failed at vbox.pl line 48. (Where I use pack_start)
GLib-GObject-WARNING **: invalid (NULL) pointer instance at vbox.pl line 67.
GLib-GObject-CRITICAL **: g_signal_connect_closure: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed at vbox.pl line 67. (the signal_connect doesn't work)

显然这不适用于复杂对象。
我尝试了另一种修复方法,轮询主(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) 我必须在第二个线程上注释 join

#$jobB->join();

否则小程序将崩溃。
2)显然它可以工作,但是当我第二次单击重新启用按钮时,线程创建使应用程序崩溃,

这非常不稳定。 我认为 Perl 更基于 C,但这种巨大的不稳定性在 C/C++ 中完全不存在。 我有点失望。
有人有更多建议吗? Perl 中的多线程 API 是如此不稳定吗?

最新更新。 这段代码有效:

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上资源不是很密集,但优雅......应该只依赖操作系统同步原语)
2)我无法加入线程,否则小程序会崩溃
3) 鉴于 (​​2) 每次我按下按钮时都会出现巨大的内存泄漏

老实说,我看到的越多,我就越相信对于正确的应用程序开发,你不能依赖 Perl...但即使是从原型 -明智的观点有点糟糕。
我希望我做错了什么......在这种情况下,有人可以帮助我吗?

干杯,

I'd like to write a Perl GTK+ application which will:

0.1) Press button A
0.2) Disable A
0.3) start threads 1 and 2
0.4) start thread 3

Thread 3 does the following:

3.1) join thread 1
3.2) join thread 2
3.3) Enable A

On completion of thread 3, the button A should be enabled again.

Now, this kind of approach is perfectly valid in C/C++ under Win32, Linux using native GUI libraries and/or GTK+, KDE. Problem with GTK+ and Perl is that you can't share the button variable within threads (eg. point 3.3 can't be performed by thread 3).

The problem is that threads::shared works only on base types, not on references like Gtk2::Button.

I tried to bless the Gtk2::Button object again (as shown in the docs), but I got an error:

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

Is my code ok?
I'm asking because when it runs the GUI won't display the Thread button and report the following error:

Gtk-CRITICAL **: gtk_box_pack: assertion `GTK_IS_WIDGET (child)' failed at vbox.pl line 48. (Where I use pack_start)
GLib-GObject-WARNING **: invalid (NULL) pointer instance at vbox.pl line 67.
GLib-GObject-CRITICAL **: g_signal_connect_closure: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed at vbox.pl line 67. (the signal_connect doesn't work)

Apparently this doesn't work with complex objects.
I've tried another fix, polling for the running threads inside a callback function invoked in the main (GTK) thread:

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

Please note the following things:
1) I have to comment the join on the second thread

#$jobB->join();

Otherwise the applet will crash.
2) Apparently it works, but when I click on the re-enabled button for the second time, the thread creation crahses the application

This is a lot unstable. I thought Perl was more C based, but this huge instability is totally absent in C/C++. I'm a bit disappointed.
Does anyone have more suggestions?
Is the multithread API such unnstable in Perl?

Latest update.
This code works:

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

But:
1) I have to poll for threads (not very resources intensive on modern CPUs but NOT elegant ... one should rely only on OS sync primitives)
2) I can't join threads otherwise the applet crashes
3) Given (2) there are huge memory leaks every time I push the button

Honestly the more I see this the more I'm convinced that for proper app dev you can't rely on Perl...but even from a prototype-wise point of view it kinda sucks.
I hope I'm doing something wrong...in this case, could anyone please help me?

Cheers,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

唱一曲作罢 2024-08-08 21:41:10

正如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';

另一种替代方法是将回调传递给 $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,
);

两个版本的代码都会生成输出:

thread 1 started
thread 2 started
thread 1 finished
thread 2 finished
buttonA is enabled

As explained in the threads::shared docs, you need to re-bless shared objects.

Update: Try the following variation

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

Another alternative is to pass a callback to $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,
);

Both versions of the code produce the output:

thread 1 started
thread 2 started
thread 1 finished
thread 2 finished
buttonA is enabled
难以启齿的温柔 2024-08-08 21:41:10

我读过几个关于 perl 中线程和 GTK 的示例,但它们都初始化 工作线程 线程,然后它们将其状态切换为运行/停止...
并发开发的非常糟糕的例子。

还有更多建议吗?

干杯,

I've read a couple of examples about threads and GTK in perl, but all of them initialize worker threads and then they'll switch their status to run/halt...
Very bad example of concurrent development.

Any more suggestions?

Cheers,

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文