跨多个独立进程使用Python RLocks

时间:2016-06-07 19:46:47

标签: python django celery python-multiprocessing

我正在开发一个使用Celery来安排一些长期任务的Django项目。 Django和Celery都运行在完全独立的进程中,需要一种方法来协调对数据库的访问。我想使用Python的multiprocessing.RLock类(或等效类),因为我需要锁可以重入。

我的问题是,如何为单独的流程提供对RLock的访问?

我发现的两个最佳解决方案(posix_ipc modulefcntl)仅限于基于Unix的系统,我们希望避免将自己局限于此。

是否有跨平台的方式来共享进程之间的锁,而没有共同的祖先进程?

1 个答案:

答案 0 :(得分:0)

我最终使用RabbitMQ作为创建分布式锁的方法。有关如何执行此操作的详细信息,请访问RabbitMQ的博客:https://www.rabbitmq.com/blog/2014/02/19/distributed-semaphores-with-rabbitmq/

简而言之,您为锁创建一个RabbitMQ队列并向其发送一条消息。要获取锁定,请在队列上运行basic_get(非阻塞)或basic_consume(阻塞)。这将从队列中删除消息,从而阻止其他线程获取锁定。一旦你的工作完成,发送否定的ack将导致RabbitMQ重新排队消息,允许下一个线程继续。

不幸的是,这不允许重入锁定。

上面引用的链接提供了有关如何执行此操作的Java代码。弄清楚如何将其翻译成Python / Pika很烦人,以为我应该在这里发布一些示例代码。

产生锁定:

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()

获取锁定:

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")