这是生产者消费型的疯狂实施吗?

时间:2011-09-13 06:44:23

标签: python observer-pattern producer-consumer

# file1.py

class _Producer(self):

  def __init__(self):
    self.chunksize = 6220800
    with open('/dev/zero') as f:
      self.thing = f.read(self.chunksize)
    self.n = 0
    self.start()

  def start(self):
    import subprocess
    import threading

    def produce():
      self._proc = subprocess.Popen(['producer_proc'], stdout=subprocess.PIPE)
      while True:
        self.thing = self._proc.stdout.read(self.chunksize)
        if len(self.thing) != self.chunksize:
          msg = 'Expected {0} bytes.  Read {1} bytes'.format(self.chunksize, len(self.thing))
          raise Exception(msg)
        self.n += 1

    t = threading.Thread(target=produce)
    t.daemon = True
    t.start()
    self._thread = t

  def stop(self):
    if self._thread.is_alive():
      self._proc.terminate()
      self._thread.join(1)

producer = _Producer()
producer.start()

我已经编写了一些或多或少像上述设计的代码,现在我希望能够通过以下方式在其他文件中使用producer_proc的输出:

# some_other_file.py
import file1
my_thing = file1.producer.thing 

其他多个消费者可能会抓取对file.producer.thing的引用,他们都需要使用相同的producer_proc。永远不应该阻止producer_proc。这是一个理智的实现吗? python GIL是否使线程安全,或者我是否需要使用Queue重新实现获取工作线程的数据?消费者是否需要明确制作该物品的副本?

我想我正在尝试实现像Producer / Consumer模式或O​​bserver模式这样的东西,但我并不清楚设计模式的所有技术细节。

  • 单个制作人不断制作
  • 多个消费者在任意时间使用东西
  • producer.thing一旦新的可用,就应该换成新鲜的东西,大多数东西都会被闲置但是没关系
  • 多个消费者可以阅读相同的内容,或连续两次阅读相同的内容。他们只是想确定他们在被要求时得到了最新的东西,而不是一些陈旧的东西。
  • 消费者应该能够继续使用某个东西,只要它们具有范围,即使生产者可能已经用新的东西覆盖了他的self.thing

1 个答案:

答案 0 :(得分:1)

鉴于您的(不寻常!)要求,您的实施似乎是正确的。特别是,

  • 如果您只更新一个属性,那么Python GIL就足够了。单字节码指令是原子的。
  • 如果你做了更复杂的事情,请添加锁定!无论如何,它基本上是无害的 - 如果你关心性能或多核可扩展性,你可能不会使用Python!
  • 请特别注意,此代码中的self.thingself.n会在单独的字节码指令中更新。 GIL可以在两者之间发布/获取,因此除非添加锁定,否则无法获得两者的一致视图。如果您不打算这样做,我建议删除self.n,因为这是一个“有吸引力的滋扰”(容易被误用)或者至少在此警告中添加评论/文档字符串。
  • 消费者无需复印。你永远不会改变self.thing指向的特定对象(并且不能使用字符串对象;它们是不可变的)并且Python是垃圾收集的,所以只要消费者抓住它的引用,它可以继续访问它而不必担心其他线程正在做什么。可能发生的最糟糕的事情是你的程序使用来自几代self.thing的大量内存保持活着。

我有点好奇你的要求来自哪里。特别是,您并不关心thing是否从未使用或多次使用过。