Python线程,非阻塞生成

时间:2013-12-11 09:49:22

标签: python conditional-statements producer-consumer locks thread-synchronization

ThreadedWorkerQueue.add_worker()方法会阻塞,直到消耗Worker为止。是否有一个很好的设计允许在ThreadedWorkerQueue添加新工作人员而不阻止调用~.add_worker()的线程,但是仍在使用条件?

这是一个简短的SSCCE:

import time
import threading

class Worker(object):

    def work(self):
        pass

class TimeWorker(Worker):

    def __init__(self, seconds):
        super(TimeWorker, self).__init__()
        self.seconds = seconds

    def work(self):
        for i in xrange(self.seconds):
            print "Working ... (%d)" % i
            time.sleep(1)

class ThreadedWorkerQueue(threading.Thread):

    def __init__(self):
        super(ThreadedWorkerQueue, self).__init__()
        self.condition = threading.Condition()
        self.workers = []
        self.running = False

    def add_worker(self, worker):
        with self.condition:
            self.workers.append(worker)
            self.condition.notify()

    def stop(self):
        with self.condition:
            self.running = False
            self.condition.notify()
        self.join()

    def consume(self):
        if self.workers:
            worker = self.workers.pop(0)
            worker.work()

    def run(self):
        self.running = True
        while True:
            with self.condition:
                if not self.running:
                    break

                self.condition.wait()
                self.consume()

def main():
    queue = ThreadedWorkerQueue()
    queue.start()

    queue.add_worker(TimeWorker(3))
    time.sleep(1)

    tstart = time.time()
    queue.add_worker(TimeWorker(2))
    print "Blocked", time.time() - tstart, "seconds until worker was added."

    queue.stop()

main()

修改

好的,所以我最初的想法是在线程时可以唤醒条件 可以继续消费工人。这是基本原则 制片人/消费者设计,跳过连续轮询,真的只做了 当有工作要做时工作。

刚才,我有一个使用默认获取的锁的想法 在新员工被消费时释放。但我不确定这是不是 这样做的好方法。有人能发现问题(例如潜在的死锁)吗?

完整代码在GitHub上:https://github.com/NiklasRosenstein/async/blob/73828ecaa2990a71b63caf93c32f9cce5ec11d27/async.py#L686-L750

class ThreadedWorkerQueue(WorkerQueue, threading.Thread):
    r""" This class implements the consumer design, introducing :class:`Worker`
    objects to start working as soon as there are new workers available. Every
    object adding Workers to this queue are the producers. """

    def __init__(self):
        WorkerQueue.__init__(self)
        threading.Thread.__init__(self)
        self.lock = threading.Lock()

    def __del__(self):
        if self.running:
            self.stop()
            warnings.warn("ThreadedWorkerQueue not stopped before its lost.")

    def notify(self):
        r""" Notify the ThreadedWorkerQueue that processing can be continued.
        It is usually not necessary to call this method manually. """

        try:
            self.lock.release()
        except (thread.error, RuntimeError):
            pass

    def stop(self, join=True, clear=False):
        r""" Interrupt the thread in its doing, pausing the threads actions
        until :meth:`start` is called again. All remaining workers are kept
        alive unless *clear* is specified True. """

        if clear: self.workers.clear()
        self.running = False
        self.notify()
        if join: self.join()

    def consume(self):
        r""" Just like :meth:`WorkerQueue.work_off`, but doesn't override
        the value of :prop:`running`. """

        while self.workers:
            worker = self.workers.popleft()
            self.current = worker
            worker.work()
        self.current = None

    # WorkerQueue

    def add_worker(self, worker):
        super(ThreadedWorkerQueue, self).add_worker(worker)
        self.notify()

    # threading.Thread

    def start(self):
        r""" Overrides :meth:`threading.Thread.start` to allow starting
        the ThreadedWorkerQueue multiple times. """

        threading.Thread.__init__(self)
        return threading.Thread.start(self)

    def run(self):
        self.running = True
        while True:
            self.consume()
            if not self.running: break
            self.lock.acquire()

1 个答案:

答案 0 :(得分:0)

  

......但仍在处理条件?

您不需要条件。 Python已经有一个完美的机制:Queue.Queue。将ThreadedWorkerQueue.workers从列表更改为Queue,您无需担心条件,锁定,通知等问题。这将极大地简化您的代码。

您需要替换:

  • Queue
  • 的工作人员列表
  • 使用Queue.put
  • 附加+通知
  • 等待+弹出Queue.get

并摆脱with self.condition: ...

此外,从self.join()内拨打stop()并不是一个好习惯。把它留给调用线程。如果您需要停止多个线程,您需要先将它们全部停止,然后才将它们全部加入。