简体   繁体   English

在多个独立进程中使用Python RLock

[英]Using Python RLocks across multiple independent processes

I am working on a Django project that uses Celery to schedule some long-term tasks. 我正在一个使用Celery安排一些长期任务的Django项目。 Both Django and Celery run in completely independent processes and need a way to coordinate access to the database. Django和Celery都在完全独立的进程中运行,并且需要一种协调访问数据库的方法。 I'd like to use Python's multiprocessing.RLock class (or equivalent) as I need the lock to be reentrant. 我想使用Python的multiprocessing.RLock类(或等效类),因为我需要锁可以重入。

My question is, how do I provide access to the RLock for the separate processes? 我的问题是,如何为单独的进程提供对RLock的访问?

The two best solutions I've found ( posix_ipc module and fcntl ) are limited to Unix-based systems, and we'd like to avoid restricting ourselves to that. 我发现的两个最佳解决方案( posix_ipc模块fcntl )仅限于基于Unix的系统,我们希望避免局限于此。

Is there a cross-platform way to share the locks between processes without having a common ancestor process? 是否有跨平台的方法可以在进程之间共享锁而无需使用共同的祖先进程?

I ended up using RabbitMQ as a way to create distributed locks. 我最终使用RabbitMQ作为创建分布式锁的方法。 Details on how to do this can be found on RabbitMQ's blog: https://www.rabbitmq.com/blog/2014/02/19/distributed-semaphores-with-rabbitmq/ . 有关如何执行此操作的详细信息,请参见RabbitMQ的博客: https : //www.rabbitmq.com/blog/2014/02/19/distributed-semaphores-with-rabbitmq/

In short, you create a RabbitMQ queue for the lock and send a single message to it. 简而言之,您将为该锁创建RabbitMQ队列并向其发送一条消息。 To acquire the lock, run a basic_get (non-blocking) or basic_consume (blocking) on the queue. 要获取锁,请在队列上运行basic_get (非阻塞)或basic_consume (阻塞)。 This removes the message from the queue, preventing other threads from acquiring the lock. 这样会将消息从队列中删除,从而防止其他线程获取锁。 Once your work is finished, sending a negative ack will cause RabbitMQ to requeue the message, allowing the next thread to continue. 工作完成后,发送否定确认将使RabbitMQ重新排队该消息,从而允许下一个线程继续。

Unfortunately, this doesn't allow for reentrant locks. 不幸的是,这不允许重入锁。

The link referenced above gives Java code for how to go about doing this. 上面引用的链接提供了执行此操作的Java代码。 Figuring out how to translate this into Python/Pika was annoying enough that I thought I should post some example code here. 弄清楚如何将其转换为Python / Pika很烦人,以至于我认为我应该在此处发布一些示例代码。

To produce the lock: 产生锁:

import pika

with pika.BlockingConnection(pika.ConnectionParameters('localhost')) as connection:
    channel = connection.channel()
    channel.queue_declare(queue="LockQueue")
    channel.basic_publish(exchange='', routing_key='LockQueue', body='Lock')
    channel.close()

Acquiring the lock: 获取锁:

import pika
import time

def callback(ch, method, properties, body):
    print("Got lock")

    for i in range(5, 0, -1):
        print("Tick {}".format(i))
        time.sleep(1)

    print("Releasing lock")
    ch.basic_nack(delivery_tag=method.delivery_tag)
    ch.close()  # Close the channel to continue on with normal processing. Without this, `callback` will continue to request the lock.

with pika.BlockingConnection(pika.ConnectionParameters('localhost')) as connection:
    channel = connection.channel()

    channel.queue_declare(queue='LockQueue')
    channel.basic_qos(prefetch_count=1)
    channel.basic_consume(callback, queue='LockQueue')

    print("Waiting for lock")
    channel.start_consuming()
    print("Task completed")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM