从其他线程执行GTK函数 [英] Executing GTK functions from other threads

查看:179
本文介绍了从其他线程执行GTK函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This question is about GTK and threads. You may find it useful if your application crashes, freezes or you want to have a multithreaded GTK application.

推荐答案

主循环

要了解GTK,您必须了解2个概念.

Main Loop

In order to understand GTK you must understand 2 concepts.

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

  1. All contemporary GUIs are single-threaded. They have a thread which processes events from window system (like button, mouse events). Such a thread is called main event loop or main loop. GTK is also single threaded and not MT-safe. This means, that you must not call any GTK functions from other threads, as it will lead to undefined behaviour.

随着Gtk文档的发展,

As Gtk documentation sates,

与所有GUI工具包一样,GTK +使用事件驱动的编程模型.当用户什么都不做时,GTK +会坐在主循环"中并等待输入.如果用户执行了某些操作(例如,单击鼠标),则主循环醒来",并将事件传递给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是基于事件的,并且是异步的.它对按钮单击的反应不是在确切的单击瞬间,而是在稍后.

Gtk is event-based and asynchronous. It reacts to button clicks not in the exact moment of clicking, but a bit later.

可以这样粗略地写(不要在家中尝试):

It can be very roughly written like this (don't try this at home):

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将无法重绘或对用户交互做出反应.该应用程序冻结.

I want a delayed action in my app

For example, hide a tooltip in several seconds or change button text. Assuming your application is single-threaded, if you call sleep() it will be executed in main loop. sleep() means, that this particular thread will be suspended for specified amount of seconds. No work will be done. And if this thread is main thread, GTK will not be able to redraw or react to user interactions. The application freezes.

您应该做的是 schedule 函数调用.可以使用 g_timeout_add g_idle_add 在第一种情况下,上述代码段中的poll()将在几秒钟内返回此事件.在后一种情况下,如果没有更高优先级的事件将被返回.

What you should do is schedule function call. It can be done with g_timeout_add or g_idle_add In the first case our poll() from snippet above will return this event in several seconds. In the latter case it will be returned when there are no events of higher priority.

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);
}

从函数返回值非常重要.如果您不这样做,该函数将被一次又一次地调用.

Returning a value from function is very important. If you don't do it, the function will be called again and again.

如前所述,GTK不是MT安全的.您不得从其他线程调用Gtk函数. 您必须安排执行时间.与其他GTK功能不同,g_timeout_addg_idle_add 是MT安全的. 该回调将在主循环中执行.如果您在回调和线程之间有一些共享资源,则必须以原子方式读取/写入它们或使用互斥锁.

As already mentioned, GTK is not MT-safe. You must not call Gtk functions from other threads. You must schedule execution. g_timeout_add and g_idle_add are MT-safe, unlike other GTK functions. That callbacks will be executed in main loop. If you have some shared resources between callback and thread you must read/write them atomically or use a mutex.

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函数,但您不知道主循环处于哪种状态.也许就在片刻之前释放了资源.总是安排.

Follow up: but python is single-threaded and GIL et cetera?

You can imagine that python is multi-threaded application run on a single-core machine. You never know when the threads will be switched. You call a GTK function but you don't know in which state the main loop is. Maybe it free'd resources just a moment before. Always schedule.

  • 有关glib主循环的详细文档,请参见
  • Detailed documentation on glib main loop can be found here
  • GSource as a more low-level primitive.
  • GTask

这篇关于从其他线程执行GTK函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆