gpt4 book ai didi

multithreading - 从其他线程执行 GTK 函数

转载 作者:行者123 更新时间:2023-12-03 12:58:40 31 4
gpt4 key购买 nike

这个问题是关于 GTK 和线程的。
如果您的应用程序崩溃、卡住或者您想要一个多线程 GTK 应用程序,您可能会发现它很有用。

最佳答案

主循环
为了理解 GTK,你必须理解 2 个概念。

  • 所有当代 GUI 都是单线程的。他们有一个线程来处理来自窗口系统的事件(如按钮、鼠标事件)。
    这样一个线程叫做主事件循环 或主循环。
    GTK 也是单线程和 不是 MT 安全。这意味着,您不能从其他线程调用任何 GTK 函数,因为它会导致未定义的行为。
  • 正如 Gtk 文档所述,

  • Like all GUI toolkits, GTK+ uses an event-driven programming model. When the user is doing nothing, GTK+ sits in the “main loop” and waits for input. If the user performs some action - say, a mouse click - then the main loop “wakes up” and delivers an event to GTK+. GTK+ forwards the event to one or more widgets.


    Gtk 是基于事件和异步的。它对按钮点击的 react 不是在点击的确切时刻,而是稍后。
    它可以非常粗略地写成这样(不要在家里尝试):
    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 将无法重绘或响应用户交互。应用程序卡住。
    您应该做的是安排函数调用。可以用 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);
    }
    从函数返回值非常重要 .如果你不这样做,行为是未定义的,你的任务可能会被再次调用或删除。
    我有一个长期运行的任务
    长时间运行的任务与调用 sleep 没有区别.当一个线程忙于该任务时,它显然不能执行任何其他任务。如果那是 GUI 线程,则无法重绘界面。这就是为什么您应该将所有长时间运行的任务移至其他线程的原因。不过有一个异常(exception):非阻塞 IO,但这超出了我的回答主题。
    我有其他线程,但我的应用程序崩溃了
    如前所述,GTK 不是 MT 安全的。你不能从其他线程调用 Gtk 函数。
    您必须安排执行。 g_timeout_addg_idle_add MT 安全,不像其他 GTK 函数。
    该回调将在主循环中执行。如果回调和线程之间有一些共享资源,则必须以原子方式读取/写入它们或使用互斥锁。
    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 ("Current data value: %i", value);
    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);
    }
    }
    确保尽可能少地保留互斥锁。 想象一下,您在另一个线程中锁定了一个互斥锁并执行了一些 IO。主循环将被卡住,直到互斥锁被释放。有 g_mutex_try_lock()立即返回,但它会带来额外的同步问题,因为当主循环尝试锁定互斥锁时,您无法保证它会被解锁。
    跟进:但是 python 是单线程的,GIL 等等?
    你可以想象 python 是运行在单核机器上的多线程应用程序。
    你永远不知道线程什么时候会切换。您调用了一个 GTK 函数,但您不知道主循环处于哪个状态。也许它刚刚释放了资源。总是安排。
    没有讨论和进一步阅读的内容
  • 可以找到关于 glib 主循环的详细文档 here
  • GSource作为更底层的原语。
  • GTask
  • 关于multithreading - 从其他线程执行 GTK 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60949269/

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