美文网首页
Python和GNOME平台的线程/并发

Python和GNOME平台的线程/并发

作者: 霡霂976447044 | 来源:发表于2020-06-09 09:15 被阅读0次

原文:Threads/Concurrency with Python and the GNOME Platform
在主循环中不应该去做一些阻塞操作,因为主循环负责处理输入和绘制,阻塞它会导致用户界面卡死,对用户来说会导致用户的操作得不到任何响应。
可能的阻塞操作有以下几个:

  • 从网络上加载一些图片资源
  • 在本地文件系统里面查找一些东西
  • 读写拷贝文件
  • 一些运行时的计算依赖于其它因素
    下面的例子演示了以下几点:
  • 和GTK并行执行的Python的线程如何和UI交互
  • 如何在不使用线程的情况下将长时间运行的任务与GTK+事件处理交互
  • 如何在glib中使用和控制异步I/O操作

Threads

第一个例子显示了使用Python线程去执行代码,但是窗口上的进度条可以正常运行。

import threading
import time

from gi.repository import GLib, Gtk, GObject


def app_main():
    win = Gtk.Window(default_height=50, default_width=300)
    win.connect("destroy", Gtk.main_quit)

    progress = Gtk.ProgressBar(show_text=True)
    win.add(progress)

    def update_progress(i):
        progress.pulse()
        progress.set_text(str(i))
        return False

    def example_target():
        for i in range(50):
            GLib.idle_add(update_progress, i)
            time.sleep(0.2)

    win.show_all()

    thread = threading.Thread(target=example_target)
    thread.daemon = True
    thread.start()


if __name__ == "__main__":
    # Calling GObject.threads_init() is not needed for PyGObject >= 3.10.2
    GObject.threads_init()

    app_main()
    Gtk.main()

这个例子展示了一个简单的窗口,窗口上有一个进度条组件。一且准备好之后会创建一个python的线程,然后Gtk的主循环运行起来,窗口依然是可以交互的。GLib.idle_add()传递了回调和接口,因为Gtk线程是不安全的,只有主线程才能更新ui。
GLib.idle_add

    """    Adds a function to be called whenever there are no higher priority
    events pending to the default main loop. The function is given the
    default idle priority, #G_PRIORITY_DEFAULT_IDLE.  If the function
    returns %FALSE it is automatically removed from the list of event
    sources and will not be called again.
    
    See [memory management of sources][mainloop-memory-management] for details
    on how to handle the return value and memory management of @data.
    
    This internally creates a main loop source using g_idle_source_new()
    and attaches it to the global #GMainContext using g_source_attach(), so
    the callback will be invoked in whichever thread is running that main
    context. You can do these steps manually if you need greater control or to
    use a custom main context.
    @param function: function to call
    @param data: data to pass to @function.
    @type function: SourceFunc
    @type data: gpointer
    @returns: the ID (greater than 0) of the event source.
    @rtype: int
    """

在PyGObject 3.10.2之后,就不需要手动调用GObject.threads_init()了。

Threads: The Deprecated Way

实际上支持多个线程调用GTK,但是这个支持已经弃用。下面是一个例子

import threading
import time

from gi.repository import Gtk, GObject, Gdk, GLib


def app_main_deprecated():
    win = Gtk.Window(default_height=50, default_width=300)
    win.connect("delete-event", Gtk.main_quit)

    progress = Gtk.ProgressBar(show_text=True)

    def example_target():
        for i in range(50):
            Gdk.threads_enter()
            progress.pulse()
            progress.set_text(str(i))
            Gdk.threads_leave()
            time.sleep(10)

    def change_title_gdk(*args):
        win.set_title("change_title_gdk")
        return False

    Gdk.threads_add_timeout_seconds(
        GLib.PRIORITY_DEFAULT, 2, change_title_gdk, None)

    def change_title_glib(*args):
        Gdk.threads_enter()
        win.set_title("change_title_glib")
        Gdk.threads_leave()
        return False

    GLib.timeout_add_seconds(4, change_title_glib)

    def change_title_click(button):
        button.set_label("You clicked me")

    button = Gtk.Button(label="Click Me")
    button.connect("clicked", change_title_click)

    box = Gtk.Box()
    box.pack_start(button, False, True, 0)
    box.pack_start(progress, True, True, 0)
    win.add(box)
    win.show_all()

    thread = threading.Thread(target=example_target)
    thread.daemon = True
    thread.start()


if __name__ == "__main__":
    # Calling GObject.threads_init() is not needed for PyGObject 3.10.2+
    GObject.threads_init()

    Gdk.threads_init()
    Gdk.threads_enter()
    app_main_deprecated()
    Gtk.main()
    Gdk.threads_leave()

GObject.threads_init()会创建一个全局锁,保证资源正常抢夺,调用Gdk.threads_enter()获取锁和Gdk.threads_leave()释放锁。
注意 这些方法在GTK3.6都已经遗弃了,参见Gdk Threads API
当主循环已经开始的时候,你有两种方法运行你的代码:

  • 第一种方式是连接一个信号
  • 第二种方式是在主循环里面添加调度函数,例如,使用GLib.idle_add()GLib.timeout_add()

相关文章

网友评论

      本文标题:Python和GNOME平台的线程/并发

      本文链接:https://www.haomeiwen.com/subject/bitaictx.html