gpt4 book ai didi

linux - 销毁类实例不会杀死它在 vala 中拥有的实例

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:26:51 24 4
gpt4 key购买 nike

我有一个 Gtk.Box 的子类,它包含一个在给定时间间隔后触发通知的 GLib.Timer。我在这个类中有调用 Gtk.Box 上的 this.destroy() 的方法。即使在其父实例被销毁后,计时器仍会继续运行并触发通知。已销毁的此类的所有实例都表现出这种行为,并继续使用 CPU 和内存,直到进程被终止。

我该如何解决这个问题?如何有效地终止实例以及如何手动释放内存而不是依赖 vala 的垃圾收集。

编辑:这是一个(尴尬的)mvce

// mvce_deletable
// nine
// 2017.01.11
// valac --pkg gtk+-3.0 --pkg glib-2.0 deletablebox.vala

using Gtk;
using GLib;

class RemovableBox : Gtk.Box {
private Gtk.Button delete_button;
private GLib.Timer timer;
private Gtk.Label label;

public RemovableBox () {
delete_button = new Gtk.Button.with_label ("DESTROY");
delete_button.clicked.connect (()=>{this.destroy();});
this.add (delete_button);
label = new Gtk.Label ("0000000");
this.add (label);
timer = new GLib.Timer ();
timer.start ();
Timeout.add (50, update);
this.show_all ();
}

private bool update () {
if (timer.elapsed () > 10.0f) {
stdout.printf("and yet it breathes\n");
}
label.set_text ("%f".printf(timer.elapsed()));
return true;
}
}

int main ( string [] args ) {
Gtk.init(ref args);
var window = new Gtk.Window ();
window.destroy.connect (Gtk.main_quit);
var delete_me = new RemovableBox ();
window.add ( delete_me );
window.show_all();
Gtk.main();
return 0;
}

我在 RemovableBox 类中添加了一个 timer_id,但它仍然无法正常工作。

class RemovableBox : Gtk.Box {
private Gtk.Button delete_button;
private uint timeout_id;
private GLib.Timer timer;
private Gtk.Label label;

public RemovableBox () {
delete_button = new Gtk.Button.with_label ("DESTROY");
delete_button.clicked.connect (()=>{this.destroy();});
this.add (delete_button);
label = new Gtk.Label ("0000000");
this.add (label);
timer = new GLib.Timer ();
timer.start ();
timeout_id = Timeout.add (40, update);
this.show_all ();
}

~ RemovableBox () {
Source.remove (timeout_id);
}

private bool update () {
if (timer.elapsed () > 10.0f) {
stdout.printf("and yet it breathes\n");
}
label.set_text ("%f".printf(timer.elapsed()));
return true;
}
}

最佳答案

GLib.Timer是一个返回耗时的秒表。它不生成事件,但 GLib.Timeout

GLib 使用事件循环。这与 GTK+ 相同,它使用相同的底层 GLib 事件循环。 GLib.Timeout 用于创建一种事件源 - 一个在给定时间间隔后触发的计时器。当您的程序创建事件源时,您将获得该源的标识符。例如:

timer_id = Timeout.add_seconds (1, my_callback_function);

您的程序需要做的是将该计时器标识符存储在对象中,然后在调用按钮单击处理程序时您可以将计时器作为事件源删除:

Source.remove (timer_id);

严格来说,Vala 没有垃圾回收周期。其他语言将收集不再使用的引用,然后在清理周期中删除分配给它们的资源。 Vala 使用引用计数,但它是确定性的。所以当一个对象不再被使用时,通常是当它超出作用域时,分配给该对象的资源会立即被移除。对于 Vala 中的普通对象,而不是紧凑类,在对象被释放时也会调用析构函数。这允许在 Vala 中有效地使用资源分配初始化 (RAII) 模式。

通常你不应该手动释放对象,Vala 的引用计数非常好。我认为了解 GLib 的事件循环和事件源以了解正在发生的事情很重要。有关详细说明,请参阅 GLib's documentation on its main event loop .

现在您已经提供了一个 MCVE我们可以详细看看 Vala 是如何管理内存的。如果您想深入了解幕后发生的事情,可以将 --ccode 开关与 valac 一起使用。

您的程序中第一行感兴趣的是:

Timeout.add(50,更新);

查看 valac 中的 C 代码,此行使用 g-timeout-add-full ()功能:

g_timeout_add_full (G_PRIORITY_DEFAULT, (guint) 50, _removable_box_update_gsource_func, g_object_ref (self), g_object_unref);

这里的关键部分是g_object_ref (self)。这会将对象的引用计数增加 1,并传递一个指向该对象的指针。这很有意义,因为在 Vala 代码中传递的 update () 回调使用了来自对象的实例数据。 Vala 正在做正确的事情,并确保在定时器运行时实例数据保持事件状态。删除源时调用“g_object_unref”。这是您的程序的修改版本,将这种理解付诸实践:

// mvce_deletable
// nine
// 2017.01.11
// valac --pkg gtk+-3.0 deletablebox.vala

using Gtk;

class RemovableBox : Gtk.Box {
private Gtk.Button delete_button;
private uint timeout_id;
private GLib.Timer timer;
private Gtk.Label label;

public RemovableBox () {
delete_button = new Gtk.Button.with_label ("DESTROY");
delete_button.clicked.connect (()=>{this.tidy_up_and_destroy ();});
this.add (delete_button);
label = new Gtk.Label ("0000000");
this.add (label);
timer = new GLib.Timer ();
timer.start ();
timeout_id = Timeout.add (40, update);
this.show_all ();
}

~ RemovableBox () {
print ("RemovableBox destructor called\n");
}

private bool update () {
if (timer.elapsed () > 10.0f) {
stdout.printf("and yet it breathes\n");
}
label.set_text ("%f".printf(timer.elapsed()));
return true;
}

private void tidy_up_and_destroy () {
print ("RemovableBox.tidy_up_and_destroy called\n");
Source.remove (timeout_id);
this.destroy ();
}
}

void main ( string [] args ) {
Gtk.init(ref args);
var window = new Gtk.Window ();
window.window_position = WindowPosition.CENTER;
window.resize (250,50);
window.destroy.connect (Gtk.main_quit);
window.add (new RemovableBox ());
window.show_all();
Gtk.main();
}

以前程序仍然保留对 RemovableBox 对象的引用,因此从未被完全删除。通过先删除事件源然后调用 this.destroy(); 这意味着不再有引用并且对象被删除。

这里还有一点很重要。行:

var delete_me = new RemovableBox ();
window.add ( delete_me );

main () 中已更改为:

window.add (new RemovableBox ());

Vala 对象存在于创建它们的 block 的范围内。通过将对象分配给 delete_me,您可以为 main ()< 的其余部分保留对该对象的引用 block 。通过将其更改为方法调用的参数,它仅用于调用,因此在单击按钮时将被释放。

顺便说一句,GLib 在使用 valac 时会自动包含,因此不需要 using GLib; 或使用 编译--pkg glib-2.0.

关于linux - 销毁类实例不会杀死它在 vala 中拥有的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41561077/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com