如何在 Python GObject Introspection 应用程序中运行异步任务

Dav*_*lla 16 python gtk application-development pygi

我正在编写一个 Python + GObject 应用程序,它需要在启动时从磁盘读取大量数据。数据是同步读取的,大约需要10秒才能完成读取操作,在此期间UI的加载会有延迟。

我想异步运行任务,并在它准备好时收到通知,而不会阻塞 UI,或多或少像:

def take_ages():
    read_a_huge_file_from_disk()

def on_finished_long_task():
    print "Finished!"

run_long_task(task=take_ages, callback=on_finished_long_task)
load_the_UI_without_blocking_on_long_task()
Run Code Online (Sandbox Code Playgroud)

我过去曾将GTask用于此类事情,但我担心它的代码已经 3 年没有被触及,更不用说移植到 GObject Introspection 了。最重要的是,它在 Ubuntu 12.04 中不再可用。所以我正在寻找一种简单的方法来异步运行任务,无论是标准 Python 方式还是 GObject/GTK+ 标准方式。

编辑:这是一些代码,其中包含我正在尝试执行的操作的示例。我已经python-defer按照评论中的建议进行了尝试,但是我无法异步运行长任务并让 UI 加载而不必等待它完成。浏览测试代码

是否有一种简单且广泛使用的方式来运行异步任务并在它们完成时得到通知?

xub*_*tix 15

您的问题很常见,因此有很多解决方案(棚、多处理或线程队列、工作池……)

由于它是如此常见,因此还有一个名为 concurrent.futures的 Python 内置解决方案(在 3.2 中,但在此处反向移植:http : //pypi.python.org/pypi/futures)。'Futures' 有多种语言版本,因此 python 对它们的称呼相同。这是典型的调用(这是您的完整示例,但是,db 部分被 sleep 替换,请参阅下面的原因)。

from concurrent import futures
executor = futures.ProcessPoolExecutor(max_workers=1)
#executor = futures.ThreadPoolExecutor(max_workers=1)
future = executor.submit(slow_load)
future.add_done_callback(self.on_complete)
Run Code Online (Sandbox Code Playgroud)

现在解决您的问题,这比您的简单示例所建议的要复杂得多。一般来说,你有线程或进程来解决这个问题,但这就是你的例子如此复杂的原因:

  1. 大多数 Python 实现都有一个 GIL,这使得线程不能充分利用多核。所以:不要在 python 中使用线程!
  2. 你想slow_load从数据库返回的对象是不可pickelable的,这意味着它们不能简单地在进程之间传递。所以:没有软件中心结果的多重处理!
  3. 您调用的库(softwarecenter.db)不是线程安全的(似乎包括 gtk 或类似的),因此在线程中调用这些方法会导致奇怪的行为(在我的测试中,从“它可以工作”到“核心转储”到简单没有结果就放弃)。所以:软件中心没有线程。
  4. gtk 中的每个异步回调都不应该做任何事情,除了调度一个将在 glib 主循环中调用的回调。所以:不print,没有 gtk 状态改变,除了添加回调!
  5. Gtk 等不适用于开箱即用的线程。您需要这样做threads_init,如果您调用 gtk 或类似方法,则必须保护该方法(在早期版本中,这是gtk.gdk.threads_enter(), gtk.gdk.threads_leave(). 参见 gstreamer:http : //pygstdocs.berlios.de/pygst-tutorial/playbin。 html)。

我可以给你以下建议:

  1. 重写你slow_load的返回pickelable结果并使用带有流程的期货。
  2. 从 softwarecenter 切换到 python-apt 或类似的(你可能不喜欢那样)。但是由于您受雇于 Canonical,您可以直接要求软件中心开发人员将文档添加到他们的软件中(例如声明它不是线程安全的),甚至更好,使软件中心线程安全。

作为一个说明:由别人给出的解决方案(Gio.io_scheduler_push_jobasync_call与工作time.sleep,但不是softwarecenter.db。这是因为这一切都归结为线程或进程和线程不能与 gtk 和softwarecenter.


Sie*_*ter 10

这是使用 GIO 的 I/O 调度程序的另一个选项(我以前从未在 Python 中使用过它,但下面的示例似乎运行良好)。

from gi.repository import GLib, Gio, GObject
import time

def slow_stuff(job, cancellable, user_data):
    print "Slow!"
    for i in xrange(5):
        print "doing slow stuff..."
        time.sleep(0.5)
    print "finished doing slow stuff!"
    return False # job completed

def main():
    GObject.threads_init()
    print "Starting..."
    Gio.io_scheduler_push_job(slow_stuff, None, GLib.PRIORITY_DEFAULT, None)
    print "It's running async..."
    GLib.idle_add(ui_stuff)
    GLib.MainLoop().run()

def ui_stuff():
    print "This is the UI doing stuff..."
    time.sleep(1)
    return True

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)