thread.start_new_thread(update())
是错误的。它会立即在主线程中调用update()
,你不应该直接使用thread
模块,而应该使用threading
模块。
你可以调用threading.current_thread()
来找出哪个线程执行了update()
。
为了简化你的代码,你可以在主线程中运行所有gtk代码,并使用阻塞操作来检索网页并在后台线程中运行它们。
基于GTK+ 3教程中的扩展示例:
import threading
import urllib2
from Queue import Queue
from gi.repository import Gtk, GObject
UPDATE_TIMEOUT = .1
_lock = threading.Lock()
def info(*args):
with _lock:
print("%s %s" % (threading.current_thread(), " ".join(map(str, args))))
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Hello World")
self.button = Gtk.Button(label="Click Here")
self.button.connect("clicked", self.on_button_clicked)
self.add(self.button)
self.updater = Updater()
self._update_id = None
self.update()
def on_button_clicked(self, widget):
info('button_clicked')
self.update()
def update(self):
if self._update_id is not None:
GObject.source_remove(self._update_id)
self.updater.add_update(self.done_updating)
self._update_id = GObject.timeout_add(
int(UPDATE_TIMEOUT*1000), self.update)
def done_updating(self, task_id):
info('done updating', task_id)
self.button.set_label("done updating %s" % task_id)
class Updater:
def __init__(self):
self._task_id = 0
self._queue = Queue(maxsize=100)
for _ in range(9):
t = threading.Thread(target=self._work)
t.daemon = True
t.start()
def _work(self):
opener = urllib2.build_opener()
for task_id, done, args in iter(self._queue.get, None):
info('received task', task_id)
try:
data = opener.open('http://localhost:5001').read()
except IOError:
pass
GObject.idle_add(done, *((task_id,) + args))
def add_update(self, callback, *args):
self._task_id += 1
info('sending task ', self._task_id)
self._queue.put((self._task_id, callback, args))
GObject.threads_init()
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
注意:
GObject.idle_add()
是唯一从不同线程调用的与 gtk 相关的函数。
另请参见
多线程 GTK 应用程序 - 第 1 部分:误解。
self
参数。 - jfs