You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python threading中如何正确处理竞态条件且不影响程序运行性能

问题根源梳理

你当前遇到的竞态问题和锁失效的核心原因有三个:

  1. 错误共享实例可变属性:run方法循环中反复修改self.user_id、self.amount两个实例属性,如果makePurchase内直接读取实例属性而非传入参数,就会出现值还没被读取就被下一轮循环覆盖的情况,锁放在方法内部根本保护不了外部的赋值逻辑
  2. 锁的作用域错误:当前用的是Connection实例级别的锁,如果多个Connection实例操作同一个用户的数据,锁完全不共享,无法避免跨实例的竞态
  3. 无限制创建线程:每个用户购买都开新线程,用户量大时线程上下文切换开销会远大于任务本身的执行开销,反而拉低性能

解决方案

1. 消除实例属性的共享误用

不要用实例属性传递单次循环的用户数据,直接传循环变量的当前值,同时makePurchase内全部使用传入的参数,不碰实例的全局可变属性:

def run( self ):
    self.users_list = self.getUsers( self.symbol )
    if self.users_list["count"] > 0:
        for u in self.users_list["data"]:
            # 直接用局部变量传参,不要修改self的属性
            user_id = u["user_id"]
            amount  = u["amount"]
            t = Thread(target=self.makePurchase, args=(self.symbol, user_id, amount, ))
            t.start()

2. 改用用户级细粒度锁,兼顾安全和性能

使用全局用户锁字典,只让同一个用户的操作串行,不同用户的操作完全并行,不会影响整体性能:

# Connect.py 顶部新增全局锁管理逻辑
from threading import Thread, Lock
# 全局用户锁字典,key为user_id,value为对应锁对象
user_locks = {}
# 保护锁字典本身的线程安全
lock_of_locks = Lock()

def get_user_lock(user_id):
    with lock_of_locks:
        if user_id not in user_locks:
            user_locks[user_id] = Lock()
        return user_locks[user_id]

修改makePurchase的加锁逻辑,用with自动管理锁释放,避免异常导致死锁:

def makePurchase( self, symbol, user_id, amount ):
    lock = get_user_lock(user_id)
    with lock:
        # 此处写购买逻辑,同一用户的操作串行,不同用户完全并行,无额外性能损耗

3. 用线程池限制并发,避免资源耗尽

替换无限制创建线程的逻辑,用线程池控制最大并发数,降低系统调度开销:

from concurrent.futures import ThreadPoolExecutor
# 可根据服务器性能调整最大并发数,推荐设置为CPU核心数的2-4倍
MAX_WORKERS = 20

class Connection( threading.Thread ):
    # 其他逻辑不变
    def run( self ):
        self.users_list = self.getUsers( self.symbol )
        if self.users_list["count"] > 0:
            with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
                for u in self.users_list["data"]:
                    user_id = u["user_id"]
                    amount  = u["amount"]
                    executor.submit(self.makePurchase, self.symbol, user_id, amount)

线程池会自动管理线程生命周期,不需要手动join,也不会出现线程数过多导致的性能下降问题。

4. 可选数据库层面兜底

如果购买逻辑涉及数据库修改用户余额,可在数据库层面加行锁/乐观锁做双重保障,示例SQL:

UPDATE user_account SET balance = balance - %s WHERE user_id = %s AND balance >= %s

内容的提问来源于stack exchange,提问作者EricW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 18:45:04