此问题与GTK和线程有关。如果您的应用程序崩溃,死机或者您想要一个多线程GTK应用程序,您可能会发现它很有用。
为了理解GTK,您必须了解2个概念。
所有现代GUI均为单线程。它们具有一个线程,该线程处理来自窗口系统的事件(如按钮,鼠标事件)。这样的线程称为主事件循环或主循环。GTK也是单线程的,并且not MT安全。这意味着,您不得从其他线程调用任何GTK函数,因为这将导致未定义的行为。
根据Gtk文档的要求,
与所有GUI工具包一样,GTK +使用事件驱动的编程模型。当用户什么都不做时,GTK +会坐在“主循环”中并等待输入。如果用户执行某些操作(例如,单击鼠标),则主循环“醒来”,并将事件传递给GTK +。 GTK +将事件转发到一个或多个小部件。
Gtk是基于事件的,并且是异步的。它对按钮单击的反应不是在确切的单击瞬间,而是在稍后。
可以这样粗略地写(不要在家中尝试):
static list *pollable;
int main_loop (void)
{
while (run)
{
lock_mutex()
event_list = poll (pollable); // check whether there are some events to react to
unlock_mutex()
dispatch (event_list); // react to events.
}
}
void schedule (gpointer function)
{
lock_mutex()
add_to_list (pollable, something);
unlock_mutex()
}
例如,在几秒钟内隐藏工具提示或更改按钮文本。假设您的应用程序是单线程的,那么如果您调用sleep()
,它将在主循环中执行。sleep()
表示该特定线程将被挂起指定的秒数。不会做任何工作。并且如果该线程是主线程,GTK将无法重绘或对用户交互做出反应。该应用程序冻结。
您应该做的是schedule函数调用。可以使用g_timeout_add
或g_idle_add
完成在第一种情况下,上述代码段中的poll()
将在几秒钟内返回此事件。在后一种情况下,如果没有更高优先级的事件将被返回。
static int count;
gboolean change_label (gpointer data)
{
GtkButton *button = data;
gchar *text = g_strdup_printf ("%i seconds left", --count);
if (count == 0)
return G_SOURCE_REMOVE;
return G_SOURCE_CONTINUE;
}
void button_clicked (GtkButton *button)
{
gtk_button_set_label (button, "clicked");
count = 5;
g_timeout_add (1 * G_TIME_SPAN_SECOND, change_label, button);
}
从函数返回值非常重要。如果您不这样做,该函数将被反复调用。
如前所述,GTK不是MT安全的。您不得从其他线程调用Gtk函数。您必须安排执行时间。与其他GTK功能不同,g_timeout_add
和g_idle_add
static int data;
static GMutex mutex;
gboolean change_label (gpointer data)
{
GtkButton *button = data;
int value;
gchar *text;
// retrieve data
g_mutex_lock (&mutex);
value = data;
g_mutex_unlock (&mutex);
// update widget
text = g_strdup_printf ("%i seconds left", --count);
return G_SOURCE_REMOVE;
}
gpointer thread_func (gpointer data)
{
GtkButton *button = data;
while (TRUE)
{
sleep (rand_time);
g_mutex_lock (&mutex);
++data;
g_mutex_unlock (&mutex);
g_idle_add (change_label, button);
}
}
跟进:但是python是单线程的,而且GIL等吗?
未讨论什么和进一步阅读
GSource
作为更底层的原语。GTask