举个例子,假设我们的系统有一项需要大量计算的操作,如果很多用户同时执行这项操作的话,那么系统的计算资源将会被耗尽。为了保证系统的正常运作,我们可以使用计数信号量来限制在同一时间内能够执行该操作的最大用户数量。

    计数信号量(counter semaphore)跟锁非常相似,它们都可以限制资源的使用权,但是跟锁只允许单个客户端使用资源的做法不同,计数信号量允许多个客户端同时使用资源,只要这些客户端的数量不超过指定的限制即可。

    代码清单 13-7 展示了一个带有身份验证功能的计数信号量实现:

    • 在使用计数信号量之前,用户需要先通过 set_max_size() 方法设置计数信号量的最大可获取数量。

    • get_max_size() 方法和 get_current_size() 方法可以分别获取计数信号量的最大可获取数量以及当前已获取数量。

    • 获取信号量的 acquire() 方法是程序的核心:在获取信号量之前,程序会先使用两个 GET 命令分别获取信号量的当前已获取数量以及最大可获取数量,如果信号量的当前已获取数量并未超过最大可获取数量,那么程序将执行 命令,将客户端给定的标识符添加到 holders 集合里面。

    • 因为 键的值也会影响信号量获取操作的执行结果,并且这个键的值在 SADD 命令执行之前也可能会被其他客户端修改,所以程序在监视 holders 键的同时,也需要监视 max_size 键。

    • 当客户端想要释放自己持有的信号量时,它只需要把自己的标识符传给 release() 方法即可:release() 方法将调用 SREM 命令,从 集合中查找并移除客户端给定的标识符。


    代码清单 13-7 计数信号量实现:/pipeline-and-transaction/semaphore.py


    1. >>> from redis import Redis
    2. >>> client = Redis(decode_responses=True)
    3. >>> semaphore = Semaphore(client, "test-semaphore") # 创建计数信号量
    4. >>> semaphore.set_max_size(3) # 设置信号量的最大可获取数量
    5. >>> semaphore.acquire("peter") # 获取信号量
    6. True
    7. >>> semaphore.acquire("jack")
    8. True
    9. >>> semaphore.acquire("tom")
    10. >>> semaphore.acquire("mary") # 可用的三个信号量都已被获取,无法取得更多信号量
    11. False
    12. >>> semaphore.release("jack") # 释放一个信号量
    13. True
    14. >>> semaphore.get_current_size() # 目前有两个信号量已被获取
    15. 2
    16. >>> semaphore.get_max_size() # 信号量的最大可获取数量为三个
    17. 3