Python 线程,线程未被通知停止
我想在主程序停止时,让一个Python线程也停止。这个线程是用来连接服务器的,后台线程负责保持连接,而前台线程则处理回调。下面是一个简单的例子。
#!/usr/bin/python
import time, threading
class test():
running = False
def __init__(self):
print "init"
self.running = True
self.thread = threading.Thread(target = self.startThread)
self.thread.start()
def __del__(self):
running = False
print "del"
def startThread(self):
print "thread start"
while self.running:
time.sleep(1)
print "thread running"
a = test()
当程序结束时,我本以为__del__()这个方法会被调用,这样后台线程就可以收到停止的通知,但实际上它是在后台线程停止后才被调用的。因为这个类是给其他人使用的,我不想强迫他们写额外的代码来显式地调用某个函数。
4 个回答
我之前也遇到过同样的问题,最后在另一个问题的回答中找到了答案。
你可以使用 daemon = True
这个标志。关于这个的详细说明可以查看3.x 和 2.x的文档。
所以你的代码应该是这样的:
#!/usr/bin/python
import time, threading
class test():
def __init__(self):
print "init"
self.thread = threading.Thread(target = self.startThread)
self.thread.daemon = True
self.thread.start()
def startThread(self):
print "thread start"
while True:
time.sleep(1)
print("thread running")
a = test()
备注:这样做可能并不是很干净地结束你的线程,但它会在你退出程序时停止线程,我想这正是你想要的效果。
根据gnibbler的评论,使用上下文管理器来明确释放资源可能会更好。关于是否应该使用__del__
来释放资源,大家的看法似乎不太一致。关于这个话题,有几篇不错的文章可以参考,分别是这里和这里。
如果你习惯了像C++这样的语言,在那里使用的是RAII,那么可能会有点难以适应Python中析构函数的调用时机,因为它们可能不会在你预期的时候被调用,甚至可能根本不会被调用,这通常是因为引用计数和垃圾回收的工作方式。
所以,在Python中,通常的做法是使用上下文管理器,这样可以明确地释放资源。
一个简单的线程示例可能看起来像这样(未经测试):
#!/usr/bin/python
import time, threading
class test():
def __init__(self):
print "init"
self.stop_event = threading.Event()
self.thread = threading.Thread(target = self.startThread)
self.thread.start()
def startThread(self):
print "thread start"
while not self.stop_event.isSet():
time.sleep(1)
print "thread running"
def close(self):
# Request thread to stop.
self.stop_event.set()
# Wait for thread to exit.
self.thread.join()
def __enter__(self):
# Nothing to do, thread already started. Could start
# thread here to enforce use of context manager.
def __exit__(self, exc_type, exc_value, traceback):
self.close()
然后可以在上下文管理器中这样使用test()
类:
with test():
# Thread will be active here.
pass
# Thread will have been stopped and joined.
或者,使用Python的contextlib.closing辅助函数,这样可以确保在退出时调用close
。
import contextlib
with contextlib.closing(test()):
# Thread will be active here.
# But not here
__del__
这个方法不会被调用,只要还有地方在引用 self
,而在后台线程中就有这样的引用:在 def startThread(self):
的 self
参数里。
你需要把运行后台线程的函数移到类外面。而不是使用 __del__
方法,我建议你可以使用一个弱引用(weakref),像下面这样。这个代码应该可以在不使用 __del__()
方法和不需要 self.running
属性的情况下正常工作。
self.thread = threading.Thread(target=run_me, args=(weakref.ref(self),))
...
def run_me(weak_self):
"Global function"
while True:
self = weak_self()
if self is None: break # no more reference to self
...
del self # forget this reference for now
time.sleep(1)