Ash*_*win 3 gtk multithreading pygtk python-2.7
即使我没有给出 gtkMainLoop.start() 函数调用,下面的代码也会在声明 og gtkLoopTHread 本身后受到攻击。我想在后台运行计时器,并在该 UI 中执行一些操作,并在计时器达到“0”时销毁 UI。
代码
def createCountDown(maxSec):
while maxSec > 0:
print maxSec;
maxSec = maxSec - 1;
gtk.main_quit();
return;
maxSec = 5;
gtkLoopThread = threading.Thread(group=None, target=gtk.main, name=None, args=(), kwargs={});
print "After gtkThread declaration"
myThread = threading.Thread(group=None, target=createCountDown, name=None, args=(maxSec), kwargs={});
gtkLoopThread.start();
myThread.start();
Run Code Online (Sandbox Code Playgroud)
预期输出:
After gtkThread declaration
5
4
3
2
1
Run Code Online (Sandbox Code Playgroud)
当前行为: 行“在 gtkThread 声明之后”没有看到打印,因为 gtkLoopThread 在初始化 gtkLoopThread 变量后立即启动
有几个问题会阻止您的代码正常运行。
首先也是最重要的,您错过了程序开头对gobject.threads_init()(after ) 的调用。此调用会导致 PyGTK在进入主循环时import gobject释放GIL 。不调用它将使线程变得无用。(在最新的 PyGObject with Python 3 中,这不再是必要的。)
第二个问题是微不足道的,是您错误地构造了一个单元素元组作为构造函数args的参数threading.Thread- 它应该是args=(maxSec,),而不是args=(maxSec)。
经过上述两项更改后,您的程序将显示预期的输出。
最后,正如 andlabs 指出的和文档确认的那样,不允许从运行主循环的线程以外的任何线程调用 GTK 代码。工作线程可以用来gobject.idle_add调度要在 GUI 线程中执行的代码,而不是调用 GTK 函数。(如果 GUI 线程应该是空闲的,这会立即发生。)换句话说,您应该替换gtk.main_quit()为gobject.idle_add(gtk.main_quit). 如果 or 函数接受参数,请gobject.idle_add(lambda: gtk_call_here(...)改用。虽然在 GTK 程序中创建多个工作线程是完全合法的,但它们必须避免直接调用 GTK。
另外,根据文档,运行主循环的线程应该与初始化 GTK 的线程相同。因此,正确的程序不应该import gtk在 GUI 线程启动之前(对其进行初始化)。这是实现此功能的代码版本:
import threading
# Event marking the GUI thread as having imported GTK. GTK must not
# be imported before this event's flag is set.
gui_ready = threading.Event()
def run_gui_thread():
import gobject
gobject.threads_init()
import gtk
gui_ready.set()
gtk.main()
gui_thread = threading.Thread(target=run_gui_thread)
gui_thread.start()
# wait for the GUI thread to initialize GTK
gui_ready.wait()
# it is now safe to import GTK-related stuff
import gobject, gtk
def countdown(maxSec):
while maxSec > 0:
print maxSec
maxSec -= 1
gobject.idle_add(gtk.main_quit)
worker = threading.Thread(target=countdown, args=(5,))
print 'starting work...'
worker.start()
# When both the worker and gui_thread finish, the main thread
# will exit as well.
Run Code Online (Sandbox Code Playgroud)