从其他线程执行GTK函数

问题描述 投票:0回答:1

此问题与GTK和线程有关。如果您的应用程序崩溃,死机或者您想要一个多线程GTK应用程序,您可能会发现它很有用。

multithreading gtk3
1个回答
0
投票

主循环

为了理解GTK,您必须了解2个概念。

  1. 所有现代GUI均为单线程。它们具有一个线程,该线程处理来自窗口系统的事件(如按钮,鼠标事件)。这样的线程称为主事件循环或主循环。GTK也是单线程的,并且not MT安全。这意味着,您不得从其他线程调用任何GTK函数,因为这将导致未定义的行为。

  2. 根据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_addg_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_addg_idle_add 是MT安全的。回调将在主循环中执行。如果回调和线程之间有一些共享资源,则必须以原子方式读取/写入它们或使用互斥锁。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等吗?

您可以想象python是在单核计算机上运行的多线程应用程序。您永远都不知道何时将切换线程。您调用了GTK函数,但您不知道主循环处于哪种状态。也许刚才它释放了资源。总是安排。

未讨论什么和进一步阅读

    关于glib主循环的详细文档可以找到here

  • GSource作为更底层的原语。
  • GTask
  • © www.soinside.com 2019 - 2024. All rights reserved.