Python中的redis-lock包使用介绍

pip安装

# 这两个模块都得有(安装完后会附带安装其他三方包 比如 redis包)
pip3 install redis-lock==0.2.0
pip3 install python-redis-lock==4.0.0

非阻塞、非重试的分布式锁

import time
import threading
import traceback
from concurrent.futures import ThreadPoolExecutor, as_completed

from redis import Redis
from redis_lock import Lock, NotAcquired

# 初始化 Redis 客户端
redis_client = Redis(host="localhost", port=6379, db=0, decode_responses=True)

# 全局计数变量
count = 0


# 任务函数
def task_concurrent_noblock(key_name):
    """任务函数,尝试获取锁并累加"""
    thread_id = str(threading.current_thread().native_id)
    global only_count

    # TODO 注意下 expire 这个参数!设置 auto_renewal=False 不自动续期的话,设置的时间最好比实际业务处理的时间长!

    # Notice 2: 实例化的方式,记得release
    # Notice 3-1、建议使用雪花算法自己生成id传入到Lock类的初始化方法中,否则在高并发情况下不同线程生成的id可能一样
    # Notice 3-2、实际上python_redis_lock包中使用的是redis的set命令(NX=True只有一个线程设置成功)确保只有一个线程能获取到锁
    # Notice 3-3、这个id是作为value存到redis中,如果后面有相同的 key_name 的线程访问,而且生成的id跟设置线程的那个id一样,就会导致还有其他线程执行到锁里的逻辑
    # Notice 4、但是实际上测试了下:redis_lock中使用base64生成的id并发情况下也不一样!参考 gan_id_tests.py 中的代码结果~
    try:
        locker = Lock(redis_client, key_name, auto_renewal=False, expire=100)
        # 非阻塞、非等待锁... Notice blocking设置为False时,对应的timeout一定要设置为None,否则会抛出异常(源码中有判断)
        acquire_res = locker.acquire(blocking=False, timeout=None)
        print(f"线程id {thread_id} acquire 的结果: {acquire_res} \n", )  # True 或 False
        if acquire_res:
            print(f"线程id {thread_id} 获取到了锁!")
            only_count += 1
            time.sleep(0.3)
            # Notice 获取到锁才释放!(这一步很重要!不release的话,分布式锁的那个redis的key不会立刻被delete掉~除非到了TTL或者永远不过期!)
            locker.release()

        return only_count
    except Exception as e:
        print(f"线程 {thread_id} 发生了异常!{traceback.format_exc()}")

    return -1


# 主程序
if __name__ == "__main__":
    # 全局变量
    only_count = 0
    key_name = 'redis_lock_test'  # 分布式锁的 key 的名字(根据实际业务自己生成)
    tasks = []

    # 创建线程池
    with ThreadPoolExecutor(max_workers=10) as executor:
        # 提交任务
        for i in range(10):
            tasks.append(executor.submit(task_concurrent_noblock, key_name))

    # 等待所有任务完成
    for future in as_completed(tasks):
        print("future.result()", future.result())

    # 最终计数
    print("only_count: ", only_count)

输出

Failed to acquire Lock('lock:redis_lock_test').
Failed to acquire Lock('lock:redis_lock_test').
Failed to acquire Lock('lock:redis_lock_test').
Failed to acquire Lock('lock:redis_lock_test').
Failed to acquire Lock('lock:redis_lock_test').
Failed to acquire Lock('lock:redis_lock_test').
Failed to acquire Lock('lock:redis_lock_test').
Failed to acquire Lock('lock:redis_lock_test').
Failed to acquire Lock('lock:redis_lock_test').
线程id 1487316 acquire 的结果: True 
线程id 1487316 获取到了锁!
线程id 1487317 acquire 的结果: False 
线程id 1487319 acquire 的结果: False 
线程id 1487321 acquire 的结果: False 
线程id 1487324 acquire 的结果: False 
线程id 1487323 acquire 的结果: False 
线程id 1487320 acquire 的结果: False 
线程id 1487322 acquire 的结果: False 
线程id 1487315 acquire 的结果: False 
线程id 1487318 acquire 的结果: False 
future.result() 1
future.result() 1
future.result() 1
future.result() 1
future.result() 1
future.result() 1
future.result() 1
future.result() 1
future.result() 1
future.result() 1
only_count:  1

阻塞、加重试的分布式锁(TryLock)

import time
import threading
import traceback
from concurrent.futures import ThreadPoolExecutor, as_completed

from redis import Redis
from redis_lock import Lock, NotAcquired

# 初始化 Redis 客户端
redis_client = Redis(host="localhost", port=6379, db=0, decode_responses=True)

# 全局计数变量
count = 0


# 任务函数
def task_concurrent_noblock(key_name):
    """任务函数,尝试获取锁并累加"""
    thread_id = str(threading.current_thread().native_id)
    global only_count

    # TODO 注意下 expire 这个参数!设置 auto_renewal=False 不自动续期的话,设置的时间最好比实际业务处理的时间长!

    # Notice 1: with上下文的方式(因为 __enter__ 方法中默认设置了 blocking=True, 默认情况下是阻塞的锁!)
    with Lock(redis_client, key_name, auto_renewal=False, expire=100) as locker:

        print(f"线程id {thread_id} 获取到了锁!")

        # 非阻塞模式获取锁
        only_count += 1
        time.sleep(2) # TODO 模拟处理延迟...
        return only_count


# 主程序
if __name__ == "__main__":
    # 全局变量
    only_count = 0
    key_name = 'redis_lock_test'  # 分布式锁的 key 的名字(根据实际业务自己生成)
    tasks = []

    # 创建线程池
    with ThreadPoolExecutor(max_workers=10) as executor:
        # 提交任务
        for i in range(10):
            tasks.append(executor.submit(task_concurrent_noblock, key_name))

    # 等待所有任务完成
    for future in as_completed(tasks):
        print("future.result()", future.result())

    # 最终计数
    print("only_count: ", only_count)

输出: 

线程id 1485478 获取到了锁!
线程id 1485472 获取到了锁!
线程id 1485475 获取到了锁!
线程id 1485473 获取到了锁!
线程id 1485474 获取到了锁!
线程id 1485479 获取到了锁!
线程id 1485471 获取到了锁!
线程id 1485477 获取到了锁!
线程id 1485476 获取到了锁!
线程id 1485470 获取到了锁!
future.result() 8
future.result() 2
future.result() 3
future.result() 5
future.result() 7
future.result() 9
future.result() 10
future.result() 1
future.result() 6
future.result() 4
only_count:  10

❓❓“可重入”相关

123

123

posted on 2025-03-06 17:27  江湖乄夜雨  阅读(115)  评论(0)    收藏  举报