简体   繁体   English

Python在线程和进程之间同步

[英]Python synchronise between threads and processes

A bit of background: 一点背景:

I am writing a function in Django to get the next invoice number, which needs to be sequential (not gaps), so the function looks like this: 我正在Django中编写一个函数以获取下一个发票编号,该编号必须是连续的(而不是空格),因此该函数如下所示:

def get_next_invoice_number():
    """
    Returns the max(invoice_number) + 1 from the payment records
    Does NOT pre-allocate number
    """
    # TODO ensure this is thread safe
    max_num = Payment.objects.aggregate(Max('invoice_number'))['invoice_number__max']
    if max_num is not None:
        return max_num + 1
    return PaymentConfig.min_invoice_number

Now the problem is, this function only returns the max()+1 , in my production environment I have multiple Django processes, so if this function is called twice for 2 different payments (before the first record saved), they will get the same invoice number. 现在的问题是,在我的生产环境中,此函数仅返回max()+1 ,因此我有多个Django进程,因此,如果针对两次不同的付款(两次保存第一条记录之前)两次调用了此函数,它们将得到相同的结果发票号码。

To mitigate this problem I can override the save() function to call the get_next_invoice_number() to minimise the time gap between these function calls, but there is still a very tiny chance for problem to happen. 为了减轻这个问题,我可以重写save()函数来调用get_next_invoice_number()以最大程度地减少这些函数调用之间的时间间隔,但是问题发生的机会仍然很小。

So I want to implement a lock in the approve method, something like 所以我想在approve方法中实现锁定,例如

from multiprocessing import Lock
lock = Lock()

class Payment(models.Model):
    def approve(self):
        lock.acquire()
        try:
            self.invoice_number = get_next_invoice_number()
            self.save()
        except: 
            pass
        finally:
            lock.release()

So my questions are: 所以我的问题是:

  1. Does this look okay? 这样看起来还好吗?
  2. The lock is for multiprocess, how about threads? 锁是用于多进程的,线程呢?

UPDATE: 更新:

  1. As my colleague pointed out, this is not going to work when it's deployed to multiple servers, the locks will be meaningless. 正如我的同事指出的那样,当将其部署到多台服务器时,这将无法正常工作,而锁将毫无意义。
  2. Looks like DB transaction locking is the way to go. 看起来DB事务锁定是必经之路。

The easiest way to do this, by far, is with your database's existing tools for creating sequences. 到目前为止,最简单的方法是使用数据库的现有工具来创建序列。 In fact, if you don't mind the value starting from 1 you can just use Django's AutoField . 实际上,如果您不介意从1开始的值,则可以使用Django的AutoField

If your business requirements are such that you need to choose a starting number, you'll have to see how to do this in the database. 如果您的业务需求如此,您需要选择一个起始编号,则必须在数据库中查看如何执行此操作。 Here are some questions that might help. 这里有一些 问题可能会有所帮助。

Trying to ensure this with locks or transactions will be harder to do and slower to perform. 试图用锁或事务来确保这一点将变得更加困难,执行起来也更加缓慢。

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

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