python 如何在 Django 中锁定临界区?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1123200/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 21:32:19  来源:igfitidea点击:

How to lock a critical section in Django?

pythondjango

提问by stinkypyper

I can't find a good clean way to lock a critical section in Django. I could use a lock or semaphore but the python implementation is for threads only, so if the production server forks then those will not be respected. Does anyone know of a way (I am thinking posix semaphores right now) to guarantee a lock across processes, or barring that a way to stop a Django server from forking.

我找不到在 Django 中锁定关键部分的好方法。我可以使用锁或信号量,但 python 实现仅适用于线程,因此如果生产服务器分叉,则不会遵守这些。有谁知道一种方法(我现在正在考虑 posix 信号量)来保证跨进程的锁定,或者阻止这种方法来阻止 Django 服务器分叉。

采纳答案by Dustin

You need a distributed lock manager at the point where your app suddenly needs to run on more than one service. I wrote elockfor this purpose. There are bigger onesand others have chosen to ignore every suggestion and done the same with memcached.

当您的应用突然需要在多个服务上运行时,您需要一个分布式锁管理器。我为此编写了elock。有更大的,其他人选择忽略每一个建议,并用 memcached 做同样的事情。

Please don't use memcached for anything more than light advisory locking. It is designed to forget stuff.

请不要将 memcached 用于轻量级建议锁定以外的任何用途。它旨在忘记东西。

I like to pretend like filesystems don't exist when I'm making web apps. Makes scale better.

我喜欢在制作网络应用程序时假装文件系统不存在。使规模更好。

回答by takaomag

If you use RDBMS, you can use its "LOCK" mechanism. For example, while one "SELECT FOR UPDATE" transaction locks a row, the other "SELECT FOR UPDATE" transactions with the row must wait.

如果你使用 RDBMS,你可以使用它的“LOCK”机制。例如,当一个“SELECT FOR UPDATE”事务锁定一行时,其他具有该行的“SELECT FOR UPDATE”事务必须等待。

# You can use any Python DB API.
[SQL] BEGIN;

[SQL] SELECT col_name FROM table_name where id = 1 FOR UPDATE;

[Process some python code]

[SQL] COMMIT;

回答by Tal Weiss

Use the Django builtin select_for_update function.
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#select-for-update
From the docs:
Returns a queryset that will lock rows until the end of the transaction, generating a SELECT ... FOR UPDATE SQL statement on supported databases.

使用 Django 内置的 select_for_update 函数。
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#select-for-update
来自文档:
返回一个查询集,该查询集将锁定行直到事务结束,生成一个 SELECT ...支持的数据库上的 FOR UPDATE SQL 语句。

For example:

例如:

entries = Entry.objects.select_for_update().filter(author=request.user)

All matched entries will be locked until the end of the transaction block, meaning that other transactions will be prevented from changing or acquiring locks on them.

所有匹配的条目将被锁定,直到事务块结束,这意味着将阻止其他事务更改或获取对它们的锁定。

回答by Vinay Sajip

You could use simple file locking as a mutual exclusion mechanism, see my recipe here. It won't suit all scenarios, but then you haven't said much about why you want to use this type of locking.

您可以使用简单的文件锁定作为互斥机制,请在此处查看我的秘诀。它不会适合所有场景,但是您还没有说明为什么要使用这种类型的锁定。

回答by stinkypyper

I ended up going with a solution I made myself involving file locking. If anyone here ends up using it remember that advisory locks and NFS don't mix well, so keep it local. Also, this is a blocking lock, if you want to mess around with loops and constantly checking back then there is instructions in the code.

我最终采用了我自己制作的涉及文件锁定的解决方案。如果这里有人最终使用它,请记住咨询锁和 NFS 不能很好地混合,因此请将其保留在本地。此外,这是一个阻塞锁,如果您想弄乱循环并不断检查,那么代码中有说明。

import os
import fcntl

class DjangoLock:

    def __init__(self, filename):
        self.filename = filename
        # This will create it if it does not exist already
        self.handle = open(filename, 'w')

    # flock() is a blocking call unless it is bitwise ORed with LOCK_NB to avoid blocking 
    # on lock acquisition.  This blocking is what I use to provide atomicity across forked
    # Django processes since native python locks and semaphores only work at the thread level
    def acquire(self):
        fcntl.flock(self.handle, fcntl.LOCK_EX)

    def release(self):
        fcntl.flock(self.handle, fcntl.LOCK_UN)

    def __del__(self):
        self.handle.close()

Usage:

lock = DJangoLock('/tmp/djangolock.tmp')
lock.acquire()
try:
    pass
finally:
    lock.release()

回答by Rob Boyle

I did not write this article, but I found it supremely helpful faced with this same problem:

我没有写这篇文章,但我发现它在面对同样的问题时非常有帮助:

http://chris-lamb.co.uk/2010/06/07/distributing-locking-python-and-redis/

http://chris-lamb.co.uk/2010/06/07/distributing-locking-python-and-redis/

Basically you use a Redis server to create a central server that exposes the locking functionality.

基本上,您使用 Redis 服务器来创建一个公开锁定功能的中央服务器。