【问题标题】:Threads in twisted... how to use them properly?扭曲的线程......如何正确使用它们?
【发布时间】:2011-01-15 15:24:24
【问题描述】:

我需要编写一个运行两个线程的简单应用程序: - 线程 1:定时运行,假设每 1 分钟运行一次 - 线程 2:只是一个“正常”的 while True 循环,它做“东西”

如果不是按时间间隔运行的要求,我根本不会看扭曲,但简单的 sleep(60) 还不够好,结构如下:

l = task.LoopingCall(timed_thread)
l.start(60.0)
reactor.run()

在那里实现我想要的看起来真的很简单。

现在,我如何“正确”添加另一个线程?

我在这里看到两个选项:

  • 使用线程库并运行两个“python 线程”,一个执行我的 while 循环,另一个运行 reactor.run()。但谷歌似乎反对这种做法,并建议使用扭曲线程
  • 使用绞线。这就是我尝试过的方法,但不知何故这对我来说有点笨拙。

这是我想出的:

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 报告),但 'normal' 线程继续运行。 kill PID 不会摆脱它,唯一的治疗方法是 kill -9。真奇怪。

所以。我究竟做错了什么?在扭曲中实现两个线程是正确的方法吗?我不应该为扭曲而烦恼吗?还有哪些其他“标准”替代方案可以实现定时呼叫? ('标准'我的意思是我可以 easy_install 或 yum 安装它们,我不想开始从随机网页下载和使用一些随机脚本)。

【问题讨论】:

  • 你在twisted(和一般的python)中使用线程非常小心。你的“主要”在做什么? twisted 的一个常见用例是作为协议客户端/服务器,发出请求或侦听请求。反应器将处理这些请求,并假设它们是非阻塞的,将能够在特定的时间间隔触发特定的功能。
  • 线程的危险在于,如果您不小心,一个线程可能会在另一个线程使用数据时修改数据,从而导致不可预测的行为。
  • 所有 'main' 的目的只是触发这两个线程(实际上,正如您在示例中看到的那样,它只有 4 行),这两个函数都更复杂一些,但在术语上没有什么不寻常的处理另一个只是检查它,但这并不重要)。这两个线程不共享任何数据顺便说一句

标签: python multithreading twisted timedelay


【解决方案1】:

假设你的 main 是相对非阻塞的:

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()
$蟒蛇bleh.py 我有 0 个统计数据 我有 33375 个统计数据 我有 66786 个统计数据 我有 100254 个统计数据 我有 133625 个统计数据 我受够了

【讨论】:

  • 你的意思是这不使用 python 线程。我怀疑它仍然被扭曲的 lib 线程化?
  • 不,这是单线程的。 Twisted 仅在您告诉它时才使用线程。阅读twistedmatrix.com/documents/current/core/howto/threading.html
  • 啊我现在明白了……嗯……那么如何制作这个“真正”的线程应用程序???您的示例可以满足需要,但受时间限制,即在某些情况下主循环可能需要一分钟以上才能执行,我不希望其中两个同时运行....
  • task.loopingCall 不会运行两次,如果另一个函数在“将”被调用时被阻塞。它会在有机会时调用(即阻塞调用已返回),并根据实际调用的时间重新安排下一次调用。
  • 如果这种调度形式不适合您的目的,那么可能可以使用带扭曲的“真实”线程,但这更复杂,具体取决于你在做。请参阅我之前评论中的操作指南。
【解决方案2】:

您没有在这里解释为什么您实际上需要线程。如果你有,我也许可以解释为什么你不需要需要它们。 ;)

除此之外,我可以确认您对事物的基本理解是正确的。不过,我可以澄清的一个可能的误解是“python 线程”和“Twisted 线程”彼此完全不同的概念。他们不是。 Python 提供了一个线程库。 Twisted 的所有线程 API 都是根据 Python 的线程库实现的。只是 API 不同。

就关机而言,您有两种选择。

  • 直接使用 Python 的线程 API 启动您的永远运行线程,并使该线程成为守护进程。即使守护线程仍在运行,您的进程也可以退出。此解决方案的一个可能问题是某些版本的 Python 存在守护线程问题,这将导致在关闭时崩溃。
  • 使用 Twisted 的 API 或 stdlib 线程 API 创建您的线程,同时使用 reactor.addSystemEventTrigger('before', 'shutdown', f) 添加一个 Twisted 关闭挂钩。在那个钩子中,与工作线程通信并告诉它关闭。例如,您可以在 Twisted 线程和您的工作线程之间共享一个 threading.Event 并使用钩子 set 它。工作线程可以定期检查是否已设置,并在发现已设置时退出。除了不会崩溃之外,这与守护线程相比还有另一个优势 - 它可以让您在进程退出之前在工作线程中运行一些清理或终结代码。

【讨论】:

  • +1 JP,您好,感谢您的帮助。我不认为我能胜任一个扭曲的支持者的任务。
  • np 马特。 :) 我认为您的回答非常好,除了处理“可能需要一分钟以上”才能完成的神秘案件。如果我们知道这些情况是什么,我们可能会建议一种方法来调整您的解决方案来处理它们并实际上消除线程的使用。
  • 见马特的回答评论中的描述。很难解释,我需要处理各种情况。以此为例(这非常接近现实):t1 甚至从 100 万个日历中读取条目,并将计划在该分钟发生的事件放入数据库表中。而已。 t2 (t3,4,...) 爬过表并从中执行指令。没有要求事件需要在那个确切的时间处理,只是它需要在那个精确的时间进入队列。所以 t2(3,4,...) 拥有世界上所有的时间,但 t1 受到限制。
  • 对不起,如果这听起来含糊不清,但这就是我必须处理的:)
  • 哦.. 为什么要在几分钟内定时.. 因为“日历”会不断更新,如果我不从中提取数据,它就会丢失。这只是实现队列的一种奇怪方式,但恐怕它超出了我的控制范围。为什么不更频繁?因为数据会以分钟的间隔准确地更新,我有两个选择:有标志 read_it (对于它们中的一百万左右 - 真的很难看)或等待一分钟并依赖它将是新数据的事实。阅读可能需要一分钟以上的时间,但在这种情况下,我会错过一个节拍 - 不好,但也不是灾难性的。
猜你喜欢
  • 2010-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多