我可以在python中创建一个锁或信号量,而不必直接作为参数传递它吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我可以在python中创建一个锁或信号量,而不必直接作为参数传递它吗?相关的知识,希望对你有一定的参考价值。

我们有一个使用多个守护进程的应用程序,包括Django,Flask和Celery。

我们使用Django rest框架来从客户端获取请求,然后使用celery异步执行它们。这意味着我可能有几个并发的python进程在运行,但我没有使用多处理或其他本机python并发处理。

不过,我希望在不同的进程中拥有共享锁。有没有办法在这种用例中使用python的Lock或Semaphore?

或者,我可以使用芹菜来处理它吗?

编辑:

对于我的用例,使用文件锁定就足够了,正如@Matias Cicero所建议的那样。

但是,我选择了@Aaron的答案,因为他提供了使用管理器,它解决了我的锁问题,并提供了多个额外的共享资源。事实上,这是满足我需求的非常有用的解决方案。

谢谢你们!

答案

您在上次评论中描述的内容基本上就是您要做的事情。您可以在启动同步管理器服务器之前创建锁,并将函数注册到将返回该锁的服务器对象。然后,当你从其他进程连接到服务器时,你调用该注册函数来返回锁对象(它实际上是锁的代理对象,但它的功能应该相同。这只会对可变容器(dicts,lists,等)代理中的值的修改可能不会更新代理本身。)

下面是创建一个包含锁并将代理对象传递给请求它的客户端的服务器的简短示例。

server.py :(首先在它自己的终端窗口中启动)

"""Multiprocessing sync manager server process"""
from multiprocessing import Lock
from multiprocessing.managers import BaseManager

class proxy_object_manager(BaseManager): pass
plock = Lock()
proxy_object_manager.register("get_lock", callable=lambda:plock)

#make sure this port is firewalled off from the rest of the world.
#  arbitrary code execution is a very real possibility if this is insecure. 
#  be sure to read up on how this all works if you intend to expose this computer to the internet
m = proxy_object_manager(('',50000), b'password')
m.get_server().serve_forever()

client.py(在单独的终端中打开其中的两个或多个,然后按一下输入,然后按另一个)

""" multiprocessing sync client example """
from multiprocessing.managers import BaseManager

# I played around a bit with making more functional subclasses
class proxy_object_client(BaseManager):
    def __init__(self, proxy_getter, *args, **kwargs):
        type(self).register(proxy_getter)
        super().__init__(*args, **kwargs)

client = proxy_object_client("get_lock", ("127.0.0.1", 50000), b"password")
client.connect()
plock = client.get_lock()
print(plock)

#test lock functionality
print("aquiring lock")
plock.acquire()
print("got lock")
input("press enter to release lock")
plock.release()
print("lock released")

您应该看到client.py的第一个实例立即获取锁,但是第二个实例在第一个实例中释放锁之前不应获取锁。

Gotchas:

  • 在我们的client.py中,plock只公开它代理的对象的公共方法,因此必须通过使用子类__enter__指定代理类型来显式公开或使其可用于私有方法(如__exit__BaseProxy处理“with”上下文)。
  • 从python 3.3开始可以使用“with”上下文来启动服务器,但是如果使用manager.start(),当你使用“with”时,你不能将lambda函数注册为callables(这是因为它们不能被腌制(也许只是在Windows上)并发送到它创建的子进程
  • 如果你要使用manager.__enter__()或“with”上下文,那么在server.py中使用if __name__ == "__main__":成语来启动服务器也是必要的,因为它们会启动一个子进程来重新加载主模块,从而有效地导致一个fork炸弹。幸运的是,多处理模块足够聪明,可以识别这个并且不会这样做,但在这种情况下,它只会引发异常。 (一般来说,如果使用manager.start(),永远不要在multiprocessing条件之外创建任何子进程。)

这是一个“server.py”的例子,它解决了(有些)这些问题:

if __name__ ...

以上是关于我可以在python中创建一个锁或信号量,而不必直接作为参数传递它吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Qt Designer 中创建一个打开 QFileDialog 的信号?

如何在 makefile 中创建目录

Python,如何在一个脚本中创建2个循环以便它可以工作

JavaScript 原型链学习原型对象

在 AWS 中创建 python 服务器而不违反免费套餐

如何在 Django 中创建一个包含两个字段的主键?