我正在开发一个使用GTK + 3的程序,并且或多或少地遵循MVC架构:
我的问题是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();
}
答案 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_pipes和continuations。它可能会帮助你思考这些术语。
答案 1 :(得分:3)
您应该停止使用信号,线程,并且只使用GTK +主循环的事件源。因此,如果您想每50分钟调用一些代码,那么只需使用g_timeout
或g_timeout_full
,如果您需要每秒刷新一次以上的UI(否则为g_idle_add
或g_timeout_add_seconds
是更好的选择)。
但是,我没有看到每20毫秒更新一次模型并每隔50毫秒显示一次结果的重点。当你计算不会显示的东西时,你只是在浪费计算能力。更新模型不应太昂贵,因为您可以在不到20ms的时间内完成。因此,如果您不关心确切的时间段,只需在连接到g_idle_add
的回调中更新模型,并在回调结束时,要求通过调用gtk_widget_queue_draw
或{{来更新UI 1}}在需要重绘的小部件上。然后它将尽可能快地计算(因此可以适应每台计算机的性能),并且不会计算未显示的内容。然后使用计时器确定两次刷新之间经过的时间。如果它太高,您可以使用gtk_widget_queue_draw_area
代替g_timeout_add
来限制刷新率,使用更长的刷新周期。