排队方法由Python中的不同线程在对象上运行

时间:2010-04-15 03:18:05

标签: python multithreading

假设我有一个类定义如下的对象:

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实例,我不想阻止一个请求,因为另一个忙。

我希望这更有意义。

感谢。

2 个答案:

答案 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模块来同步向消费者传递值,但这假定消费者想要接收每个值。您可能必须更具体地获得更好的答案。