假设我有一个类定义如下的对象:
class Command:
foo = 5
def run(self, bar):
time.sleep(1)
self.foo = bar
return self.foo
如果这个类被实例化一次,但是不同的线程正在使用不同的args传递其run方法(通过HTTP请求,单独处理),那么排队它们的最佳方法是什么?这可以在类定义本身中完成吗?
我正在使用XML RPC服务器(单独的类)。为简单起见,我们可以说它有一个Command类的实例被实例化为类变量。当Command.run()被两个独立的线程命中时,如何确保在下一个run_()方法启动之前完成一个run()方法?
我可以这样做:
while self.busy:
time.sleep(1)
self.busy = true
...
self.busy = false
return self.foo
但据我所知,这不会优先考虑最早的请求。
我意识到整个练习听起来有多余,因为我可以同步运行XML-RPC服务器。但是,长话短说,有多个Command实例,我不想阻止一个请求,因为另一个忙。
我希望这更有意义。
感谢。
答案 0 :(得分:2)
这是一种相对简单的方法(忽略异常,属性访问,特殊方法等):
import Queue
import threading
def serialize(q):
"""runs a serializer on queue q: put [-1]*4 on q to terminate."""
while True:
# get output-queue for result, a callable, its args and kwds
out_q, tocall, args, kwds = q.get()
if out_q == -1:
return
result = tocall(*args, **kwds)
out_q.put(result)
class WrapCall(object):
"""Wraps a callable to serialize calls to it."""
def __init__(self, inq, ouq, tocall):
self.inq = inq
self.ouq = ouq
self.tocall = tocall
def __call__(self, *a, **k):
self.inq.put((self.ouq, self.tocall, a, k))
if self.ouq is None:
return None
return self.ouq.get()
class WrapObj(object):
"""Wraps any object to serialize all calls to its methods."""
def __init__(self, obj):
self._o = obj
self._q = Queue.Queue()
t = threading.Thread(target=serialize, args=(self._q,))
t.setDaemon(True)
t.start()
self.t = t
def __getattr__(self, n):
"""Wraps methods of self.w into an appropriate WrapCall instance."""
towrap = getattr(self._o, n)
if not callable(towrap):
raise TypeError('Cannot wrap noncallable attribute %r (type: %s)'
% (n, type(towrap)))
q = Queue.Queue()
return WrapCall(self._q, q, towrap)
def WrapperWait(self):
"""Return only when self.t has served all pending requests."""
q = Queue.Queue()
w = WrapCall(self.__q, q, lambda: None)
return w()
使用此“序列化程序”,您可以
myobj = WrapObj(Command())
现在所有对myobj(非特殊)方法的调用都是以线程安全的方式序列化的。
对于你的特定情况,对象上只有一个方法,你可以进一步简化这个,但这是我编写和经常使用的一些已经简化的版本(支持属性获取和设置,特殊方法,等等,有点过于复杂而不值得;完整版本确实支持捕获和重新引用包装器对象方法中引发的异常,对您不关心的结果或异常的调用进行优化,以及一些调整,但是不是属性和特殊方法的序列化。)
答案 1 :(得分:0)
这取决于您计划如何消费foo
。最简单的方法是使用Python的queue
模块来同步向消费者传递值,但这假定消费者想要接收每个值。您可能必须更具体地获得更好的答案。