Python进程同步

时间:2014-11-07 16:37:58

标签: python multithreading synchronization

我正在努力建立一些“工人”。线程/进程,用一个'命令列表来填充它们。完成工作然后让他们一步一步地逐步完成命令。

更新我有一些关于我采用这种方法的问题,所以这里有一些背景:我使用这种方法进行自动化测试脚本。我正在模拟一个多用户环境,其中不同的用户正在运行指向共享资源的应用程序。我想在多个客户端的API上同时执行一系列操作。我希望能够控制每个工作人员做出的可重复测试集。如果没有同步,我将无法保证操作按照我期望的顺序执行。另一个要求(我可能没有提到)是我希望同时执行命令。例如。所有人都将大量数据写入数据库。

我在Windows 7上使用Python 2.7.5b3中的multiprocessing模块。到目前为止,我有以下示例工作,这说明了我想要做的事情。

此示例让工作人员将结果写回共享队列,因此我可以看到命令执行的顺序。

worker(worker.py):

from multiprocessing import Process, Queue, Event, Lock

class Worker(Process):

    def __init__(self, execute, q_out):
        Process.__init__(self)
        print self.name, 'init'
        self.daemon = True
        self.q_in = Queue()
        self.q_out = q_out
        self.execute = execute

    def run(self):
        print self.name, 'running'
        self.execute.wait()
        while not self.q_in.empty():
            cmd = self.q_in.get()
            self.q_out.put((self.name, cmd))

经理:

from multiprocessing import Event, Queue
from worker import Worker

if __name__ == '__main__':    

    workers = []
    syncEvent = Event()
    shared_q = Queue()
    for i in range(0,2):
        worker = Worker(syncEvent, shared_q)
        map(worker.q_in.put, ['A', 'B', 'C'])
        workers.append(worker)
        worker.start()

    syncEvent.set()

    for w in workers:
        w.join()

    while not shared_q.empty():
        print shared_q.get()

这给出了如下输出:

Worker-1 init
Worker-2 init
Worker-1 running
Worker-2 running
('Worker-1', 'A')
('Worker-1', 'B')
('Worker-1', 'C')
('Worker-2', 'A')
('Worker-2', 'B')
('Worker-2', 'C')

我想要实现的是这个输出:

Worker-1 init
Worker-2 init
Worker-1 running
Worker-2 running
('Worker-1', 'A')
('Worker-2', 'A')
('Worker-1', 'B')
('Worker-2', 'B')
('Worker-1', 'C')
('Worker-2', 'C')

我已锁定在LockRLock,但这似乎不符合要求,因为我试图让所有线程同时运行,但只是停止等到其他人都完成后再执行下一个命令。

我确信有一个很好而且简单的方法可以做到这一点,但我无法理解它是什么。有没有人对如何进行有任何建议?

3 个答案:

答案 0 :(得分:0)

在Python 3下,你可以使用multiprocessing.Barrier来做这样的事情。不幸的是,Python 2中不存在这种情况(转换的另一个原因!)。

对于Python 2,您可能希望让您的个体工作人员通知经理他们已完成(比如说)任务A.经理等待每个工作人员发出通知,然后通知所有工作人员继续下一个任务。您可以通过将两个事件交给每个工作人员而不是您当前使用的工作人员来完成此操作。其中一个事件由所有工人共享,并由经理用来通知工人。另一个事件特定于每个工作人员,用于通知经理。

如果您计划重新使用共享事件,则设置和重置事件之间存在竞争条件(即,其中一名工作人员可能会提前竞赛并在您有机会重置之前再次等待该事件)。对于共享案例,您可能希望使用Condition而不是Event。每个工作人员案例没有此问题,因为管理员可以在设置共享事件之前重置所有每个工作人员事件。

答案 1 :(得分:0)

此类应该用于同步进程。它基本上保存了线程条件下的所有进程,当最后一个worker完成时,它通知所有其他进程被唤醒并能够继续

worker.py

from multiprocessing import Process, Queue, Event, Lock

class Worker(Process):

    def __init__(self, execute, q_out, syncher):
        Process.__init__(self)
        print self.name, 'init'
        self.daemon = True
        self.q_in = Queue()
        self.q_out = q_out
        self.execute = execute
        self.syncher = syncher

    def run(self):
        print self.name, 'running'
        self.execute.wait()
        while not self.q_in.empty():
            self.syncher.check()
            cmd = self.q_in.get()
            self.q_out.put((self.name, cmd))

manager.py

from multiprocessing import Event, Queue, Condition, Lock, Value
from worker import Worker

class Synchroniser(object):
    def __init__(self, workers):
        self.workers_locked = Value('i', 0)
        self.workers = workers
        self.condition = Condition(Lock())

    def check(self):
        with self.condition:
            self.workers_locked.value += 1
            if self.workers_locked.value >= self.workers:
                self.condition.notify_all()
            else:
                self.condition.wait()
            self.workers_locked.value -= 1

if __name__ == '__main__':

    workers = []
    syncEvent = Event()
    shared_q = Queue()
    worker_num = 2
    syncher = Synchroniser(worker_num)
    for i in range(0,worker_num):
        worker = Worker(syncEvent, shared_q, syncher)
        map(worker.q_in.put, ['A', 'B', 'C'])
        workers.append(worker)
        worker.start()

    syncEvent.set()

    for w in workers:
        w.join()

    while not shared_q.empty():
        print shared_q.get()

> python manager.py 
Worker-1 init
Worker-2 init
Worker-1 running
Worker-2 running
('Worker-1', 'A')
('Worker-2', 'A')
('Worker-2', 'B')
('Worker-1', 'B')
('Worker-1', 'C')
('Worker-2', 'C')

答案 2 :(得分:0)

你似乎在这里做了一个交叉目的。一方面,您立即将所有工作分配给工人,但另一方面,您希望工人以锁步方式运行。您可以一次调度一个命令并等待所有结果再继续。然后,您可以删除任何需要完成的工作间同步。

from multiprocessing import Event, Queue
from worker import Worker

if __name__ == '__main__':    

    shared_q = Queue()
    results = []
    workers = [Worker(syncEvent, shared_q) for _ in range(2)]
    for worker in workers:
        worker.start()
    for worker in workers:
        worker.q_in.put('A')
    for _ in worker:
        results.append(shared_q.get())

    ...

    for w in workers:
        w.join()

    for r in results:
        print results