我觉得这应该不是那么困难,但到目前为止我没什么成功。
假设我有一个名为PikaClass的类,它包含pika并提供了一些商业方法。
def PikaClass(object):
def __init__(self):
# connect to the broker
self.connection = pika.SelectConnection(<connection parameters>, self.on_connect)
# ..other init stuff..
def on_connect(self, connection):
# called when the connection has been established
# ..open a channel, declare some queues, etc.
def start(self):
# start the polling loop
self.connection.ioloop.start()
def foo(self, **kwargs):
# do some business logic, e.g., send messages to particular queues
直观地说,这就是我想要实现的目的:用户创建PikaClass
的实例,在后台设置循环,然后通过调用某些业务方法与对象进行交互
p = PikaClass()
p.start()
bar = p.foo(..)
问题是p.start()在调用start()后阻止并阻止主代码与对象交互。我的第一个想法是将调用包装在一个线程中:
Thread(target=p.start()).start()
bar = p.foo(..)
但是那仍然阻止你永远不会到达p.foo(..)。文档提到你不应该在线程之间共享连接,这样可能会在某处造成问题。
我也尝试过使用AsyncoreConnection而不是SelectConnection,并直接调用_connect()(而不是使用ioloop),但没有任何效果(没有任何反应)。
那么如何在后台运行ioloop,或者至少运行我自己的ioloop?
注意:这是win64(xp)上的Python 2.6,最新的pika 0.9.4
答案 0 :(得分:8)
您正在调用'p.start'而不是将其作为参数传递。代码应该是:
Thread(target=p.start).start()
线程将在执行Thread.start时调用p.start。
我不确定这是否能解决您的问题,但它可以帮助您找到解决方案。
答案 1 :(得分:5)
这里的GIL不是问题,因为ioloop
几乎所有时间都花在 select(2)系统调用上,在此期间GIL发布,其他Python线程可以运行并执行其他任务。
最简单的方法是为每个请求设置和拆除队列连接。您可能认为这会太昂贵 - 因为它需要重新验证并且(可能)重复与每个连接的SSL协商 - 但它应该是最简单,最强大和最容易编写的,这应该是您的控制因素,除非您知道设置和拆卸实际上会损害整个应用程序的性能(最好通过测试来衡量)。
另一种方法是,只有start()
ioloop
一旦有消息要发送,并且接收回复的方法停止ioloop
,以便您的程序再次获得控制权。您可以提前ioloop
返回:
connection.ioloop.poller.open = False
,然后记住将其重新设置为True
,然后再次致电start()
等待其他回复。
答案 2 :(得分:-1)
你可能需要第二个过程,我对pika知之甚少但是如果它是纯python那么它会想要保留GIL - 记住你每个进程一次只能执行一个线程,无论如何由于用于python的精彩垃圾收集的引用计数器的限制,你拥有许多核心。
如果在if __name__ == "__main__":
块中执行/ anything / else之前启动新进程,并将其设置为等待循环中的事件,则可以向第二个进程发送执行一项工作的指令然后等待它将结果发回给你。你可能想要实现某种索引系统,这样你就可以将你的工作发送到另一个进程并忘记它,直到你需要答案为止。
如果你去洗澡,我唯一的建议是避免副作用,否则你将不得不考虑竞争条件,死锁以及GIL保护你的所有其他可怕的东西。在开始之前向您的流程发送所需的信息,或者确保在它要求您提供该数据之前不要对其进行修改。
这是少数情况下,Python会用一个礼貌的说明向你发枪,说不要瞄准你的脚。