GTK + 3 GUI随机冻结(1小时后或20分钟后)

时间:2017-11-22 19:32:19

标签: c gtk gtk3

我正在开发一个使用GTK + 3的程序,并且或多或少地遵循MVC架构:

  • 通过调用 model_update (它不调用GTK函数),每20 ms更新一次模型;
  • 通过读取模型变量,每50毫秒调用 gui_update 来更新GUI。

我的问题是GUI在随机运行时间后冻结,可能是20分钟或超过1小时,我不知道为什么。也许我应该知道一些关于GTK的事情?

注意: 使用互斥锁可以保护对模型变量的访问。

非常感谢你的帮助!!

#include <signal.h>
#include <gtk/gtk.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/prctl.h> 

void *
thread_gui(void* data)
{ 
    g_timeout_add(50, handler_timer_gui_update, NULL); // updates the GUI each 50ms
    gtk_main();
    pthread_exit(NULL);
}

gint
handler_timer_gui_update(gpointer data)
{
    gui_update();
    // gui_update reads the model and updates GUI by using
    // gtk_label_set_text, gtk_spin_button_set_value, cairo_paint
    return TRUE;
}

void
launch_periodical_call_updating_model( )
{
    signal( SIGRTMIN + 1, model_update );

    timer_t timer;
    struct sigevent event;
    event.sigev_notify = SIGEV_SIGNAL;
    event.sigev_signo = SIGRTMIN + 1;
    event.sigev_value.sival_ptr = &timer;
    timer_create(CLOCK_REALTIME, &event, &timer);

    struct itimerspec spec;
    spec.it_value.tv_nsec = 20 * 1000000; // updates the model each 20 ms
    spec.it_value.tv_sec = 0;
    spec.it_interval = spec.it_value;

    timer_settime( timerModel, 0, &spec, NULL);
}

int
main( int argc, char *argv[] )
{ 
    pthread_t pthread_gui;

    init_model( ); // init model variables
    launch_periodical_call_updating_model( ); 

    // signal capture to exit
    signal( SIGINT, ctrlc_handler );
    signal( SIGTERM, ctrlc_handler );

    // GUI
    g_thread_init(NULL);
    gdk_threads_init();
    gdk_threads_enter();
    gtk_init( &argc, &argv );
    create_gui ( ); // building GTK Window with widgets  
    pthread_create(&pthread_gui, NULL, thread_gui, NULL);
    gdk_threads_leave();

    // Leaving the program
    pthread_join( pthread_gui, NULL ); 
    stop_model( ); //It stops to update the model and releases memory

    return 0;
}

void
ctrlc_handler( int sig )
{
    gtk_main_quit(); 
} 

2 个答案:

答案 0 :(得分:3)

你不应该在GTK上使用Unix信号。请参阅signal-safety(7)signal(7)

您应该考虑仅使用Glib计时器(而不是使用任何POSIX计时器)。阅读Glib event loop(在GTK中使用)并使用g_timeout_add或相关函数。

如果您坚持在GTK程序中处理Unix信号,请执行Qt recommends关于它们的操作:在初始化时设置pipe(7)自我,并轮询该文件描述符(在GTK中,使用{{3您的Unix信号处理程序将g_source_add_unix_fd(一次只有一个或几个字节),或者g_io_channel_unix_new)。顺便说一下,Linux还提供了write(2)

最后,GTK并不是真正的线程友好的(参见signalfd(2)),并且所有GTK函数都应该(仅)从主线程(而不是其他pthread_gui)调用。实际上,如果您确实需要使用多个线程进行编码,请考虑让非GUI线程通过管道与GTK进行通信和同步,并使用this来启动和管理它们。

您甚至可以考虑一些多处理方法:在一个进程中使用GUI程序,在另一个进程中使用其他处理(多线程)(可能由GUI程序使用Glib thread functions启动)。

了解g_spawn_async_with_pipescontinuations。它可能会帮助你思考这些术语。

答案 1 :(得分:3)

您应该停止使用信号,线程,并且只使用GTK +主循环的事件源。因此,如果您想每50分钟调用一些代码,那么只需使用g_timeoutg_timeout_full,如果您需要每秒刷新一次以上的UI(否则为g_idle_addg_timeout_add_seconds是更好的选择)。

但是,我没有看到每20毫秒更新一次模型并每隔50毫秒显示一次结果的重点。当你计算不会显示的东西时,你只是在浪费计算能力。更新模型不应太昂贵,因为您可以在不到20ms的时间内完成。因此,如果您不关心确切的时间段,只需在连接到g_idle_add的回调中更新模型,并在回调结束时,要求通过调用gtk_widget_queue_draw或{{来更新UI 1}}在需要重绘的小部件上。然后它将尽可能快地计算(因此可以适应每台计算机的性能),并且不会计算未显示的内容。然后使用计时器确定两次刷新之间经过的时间。如果它太高,您可以使用gtk_widget_queue_draw_area代替g_timeout_add来限制刷新率,使用更长的刷新周期。