通过多个子进程锁定互斥锁

时间:2014-09-03 20:20:09

标签: python linux

互斥体是否在此代码中完成了它的工作?即sys.stdout.write受保护?

import sys
from multiprocessing import Pool, Lock

class ParentApp():
    mutex=Lock()
    def report(self,msg):
        with ParentApp.mutex:
            sys.stdout.write(msg)

class ChildApp1(ParentApp):
    def print_report(self):
        for i in xrange(100):
            ParentApp.report(self, 'BLABLA')


class ChildApp2(ParentApp):
    def print_report(self):
        for i in xrange(100):
            ParentApp.report(self, 'TESTTEST')

def runnable(app):
    app.print_report()

def main():
    app=[]
    app.append(ChildApp1())
    app.append(ChildApp2())

    pool = Pool(len(apps))
    pool.map(runnable, apps)

    exit(0)

if __name__ == '__main__':
    sys.exit(main())

PS:代码也在这里:http://pastebin.com/GyV3w45F

PS:我在Linux主机上运行

1 个答案:

答案 0 :(得分:1)

如果你在类似Posix的平台上,它只会做它的工作。如果您使用的是Windows,则每个进程都会以完全不同的锁副本结束。

您可以通过添加一些额外的跟踪和sleep语句来看到这一点:

class ParentApp():
    mutex=Lock()
    def report(self,msg):
        print("\nGETTING for {}".format(msg))
        with self.mutex:
            print("GOT for {}".format(msg))
            sys.stdout.write(msg)
            sys.stdout.flush()
            time.sleep(5)

在Linux上:

GETTING for BLABLA
GOT for BLABLA
BLABLA
GETTING for TESTTEST
< 5 second delay here>

在Windows上:

GETTING for BLABLA
GOT for BLABLA
BLABLA
GETTING for TESTTEST
GOT for TESTTEST
TESTTEST
<5 second delay here>

这是因为Posix平台使用os.fork()创建新进程,这意味着您在父进程中创建的Lock()会自动由子进程继承。但是,Windows没有os.fork,因此它需要生成一个新进程,然后在子进程中重新导入您的模块。重新导入模块意味着ParentApp被重新导入并重新执行,Lock类属性也是如此。因此,您的父母和两个孩子最终都拥有自己独特的Lock

要解决此问题,您需要在父级中创建一个Lock,然后将其传递给子级。对于您当前的体系结构而言,这实际上并不是一项微不足道的任务 - 您将Lock对象作为参数传递给pool.map,这将不允许您传递Lock个对象。如果您尝试,您将获得例外:

RuntimeError: Lock objects should only be shared between processes through inheritance

您实际上开始 Lock时,您只能将正常Process个对象传递给儿童。一旦它们启动(就像你调用Pool方法时那样),你会得到异常:

l = Lock()
p = Process(target=func, args=(l,)) # ok
p.start()

pool = Pool()
pool.apply(func, args=(l,)) # not ok, raises an exception.

为了将Lock传递给像Pool这样的map函数,您需要使用multiprocessing.Manager来创建共享锁。这是我建议做的事情:

import sys 
from multiprocessing import Pool, Lock, get_context, Manager
import time

class ParentApp():
    def __init__(self, mutex):
        self.mutex = mutex

    def report(self,msg):
        with self.mutex:
            sys.stdout.write(msg)

class ChildApp1(ParentApp):
    def print_report(self):
        for i in range(100):
            ParentApp.report(self, 'BLABLA')


class ChildApp2(ParentApp):
    def print_report(self):
        for i in range(100):
            ParentApp.report(self, 'TESTTEST')

def runnable(app):
    app.print_report()

def main():
    apps=[]
    m = Manager()
    lock = m.Lock()
    apps.append(ChildApp1(lock))
    apps.append(ChildApp2(lock))

    pool = Pool(len(apps))
    pool.map(runnable, apps)

if __name__ == '__main__':
    sys.exit(main())

为了确保共享Lock,我们需要让ParentApp实际上将锁定对象作为参数。这不是一个很好的,因为它在课堂上完全独立,但我认为这是我们能够做到的最好的Windows限制。