在Twisted中使用线程的正确方法?
我需要写一个简单的应用程序,运行两个线程:
- 线程1:按时间间隔运行,比如每分钟一次
- 线程2:就是一个普通的 while True 循环,做一些事情
如果不是需要按时间间隔运行,我根本不会考虑使用 twisted,但简单的 sleep(60) 方式不够好,像这样的结构:
l = task.LoopingCall(timed_thread)
l.start(60.0)
reactor.run()
看起来真的很简单,可以实现我想要的功能。
现在,我该如何“正确”地添加另一个线程呢?
我看到两种选择:
- 使用 threading 库,运行两个“python 线程”,一个执行我的 while 循环,另一个运行 reactor.run()。但是谷歌似乎不太推荐这种方法,建议使用 twisted 的线程。
- 使用 twisted 的线程。我试过这个,但感觉有点笨拙。
这是我想到的:
def timed_thread():
print 'i will be called every 1 minute'
return
def normal_thread():
print 'this is a normal thread'
time.sleep(30)
return
l = task.LoopingCall(timed_thread)
l.start(60.0)
reactor.callInThread(normal_thread)
reactor.run()
这似乎可以工作,但!我无法停止这个应用程序。如果我按 ^C,它不会有任何反应(如果没有 'callInThread',它就会像你预期的那样停止)。按 ^Z 会返回到 shell,如果我接着执行 'kill %1',似乎会杀掉进程(shell 报告了这一点),但“正常”的线程还是在运行。用 kill PID 也无法解决,唯一的办法是 kill -9。真的很奇怪。
所以,我到底做错了什么?在 twisted 中实现两个线程是正确的方法吗?我是不是不该用 twisted?还有什么其他“标准”的方法可以实现定时调用?(“标准”是指我可以用 easy_install 或 yum install 安装的,我不想去下载一些随机网页上的脚本)。
2 个回答
5
你没有解释为什么在这里需要线程。如果你解释了,我可能能告诉你其实你不需要它们。;)
抛开这些不谈,我可以确认你对事情的基本理解是正确的。不过,有一个可能的误解我可以澄清,就是“python线程”和“Twisted线程”其实没有什么不同。它们是一样的。Python提供了一个线程库,而Twisted的所有线程API都是基于Python的线程库实现的。只是API的形式不同而已。
关于关闭程序,你有两个选择。
- 直接使用Python的线程API来启动一个永远运行的线程,并把这个线程设置为守护线程。这样即使守护线程还在运行,你的程序也可以退出。不过,这种方法可能会有个问题,就是某些版本的Python在关闭时会因为守护线程而崩溃。
- 使用Twisted的API或者标准库的线程API来创建你的线程,同时添加一个Twisted的关闭钩子,使用
reactor.addSystemEventTrigger('before', 'shutdown', f)
。在这个钩子里,你可以和工作线程进行沟通,告诉它要关闭。例如,你可以在Twisted线程和工作线程之间共享一个threading.Event
,然后在钩子里调用set
。工作线程可以定期检查这个事件是否被设置,如果发现被设置了就退出。除了不会崩溃,这种方法还有一个好处,就是可以让你在程序退出之前,在工作线程里运行一些清理或收尾的代码。
2
假设你的主程序运行得比较顺畅,不会卡住:
import random
from twisted.internet import task
class MyProcess:
def __init__(self):
self.stats = []
self.lp = None
def myloopingCall(self):
print "I have %s stats" % len(self.stats)
def myMainFunction(self,reactor):
self.stats.append(random.random())
reactor.callLater(0,self.myMainFunction,reactor)
def start(self,reactor):
self.lp = task.LoopingCall(self.myloopingCall)
self.lp.start(2)
reactor.callLater(0,self.myMainFunction,reactor)
def stop(self):
if self.lp is not None:
self.lp.stop()
print "I'm done"
if __name__ == '__main__':
myproc = MyProcess()
from twisted.internet import reactor
reactor.callWhenRunning(myproc.start,reactor)
reactor.addSystemEventTrigger('during','shutdown',myproc.stop)
reactor.callLater(10,reactor.stop)
reactor.run()
$ python bleh.py I have 0 stats I have 33375 stats I have 66786 stats I have 100254 stats I have 133625 stats I'm done