如何在不同线程的类之间传递变量?
线程间安全传递变量的正确姿势
嘿,我来帮你捋捋这个线程间变量传递的问题~先给你指出代码里几个小bug,不然跑都跑不起来:
while True后面漏了冒号:,Python语法会直接报错bar类的__init__里没把传入的fooInstance存成实例变量,后面run方法根本拿不到这个引用foo的run里只是创建了bar对象,但没调用start()启动线程,这样bar线程永远不会执行
回到核心问题:不同线程类之间传递变量,最关键的是要保证共享资源的线程安全——多个线程同时读写同一个变量(比如你这里的foo.var1字典)会触发数据竞争,结果完全不可预期。下面给你两种靠谱的解决方案:
方案一:用线程锁(Lock)保护共享字典
这是最直接的方式,给共享的var1加一把锁,不管是读还是写,都先拿到锁再操作,确保同一时间只有一个线程能碰它。
修改后的代码示例:
import threading class foo(threading.Thread): def __init__(self): threading.Thread.__init__(self, name="foo=>bar") self.var1 = {} # 给var1配一把专属锁 self.var_lock = threading.Lock() def run(self): while True: value, name = getvalue() # name是字符串类型 # 写操作前先拿锁,用with自动释放,比手动acquire/release安全 with self.var_lock: self.var1[name] = value # 注意:如果循环里每次都新建bar线程,很快会线程爆炸!建议提前启动一个bar线程循环处理,或者用线程池控量 bar_thread = bar(self) bar_thread.start() class bar(threading.Thread): def __init__(self, foo_instance): threading.Thread.__init__(self, name="bar") self.foo_instance = foo_instance # 把foo实例存下来,后面才能用 def run(self): while True: arg = myfunction() # 读操作也要拿锁,避免读的时候刚好在写 with self.foo_instance.var_lock: # 先判断arg存在不,不然会KeyError if arg not in self.foo_instance.var1: continue # 或者根据业务做其他处理 val = self.foo_instance.var1[arg] # 耗时操作放锁外面,别占着锁不放,影响其他线程 myOtherfunction(val)
这里划几个重点:
- 用
with语句管理锁,就算操作过程中抛异常,锁也会自动释放,不会导致死锁 - 锁的粒度要尽量小,比如读写完立刻释放锁,别在锁里做
myOtherfunction这种耗时操作 - 别在foo的循环里每次都新建bar线程!线程创建是有开销的,搞不好会把系统资源耗光,建议改成启动一个bar线程一直循环处理任务
方案二:用队列(queue.Queue)实现生产者-消费者模式
如果你的场景是foo生产数据,bar消费数据,用队列是更优雅的选择——队列本身就是线程安全的,不用自己手动加锁。
修改后的代码示例:
import threading import queue class foo(threading.Thread): def __init__(self, data_queue): threading.Thread.__init__(self, name="foo=>bar") self.data_queue = data_queue self.var1 = {} # 如果需要保留历史数据,依然可以存,但读写要加锁 self.var_lock = threading.Lock() def run(self): while True: value, name = getvalue() # 保存到本地字典(如果需要的话) with self.var_lock: self.var1[name] = value # 把数据丢进队列,传给bar self.data_queue.put((name, value)) class bar(threading.Thread): def __init__(self, data_queue, foo_instance=None): threading.Thread.__init__(self, name="bar") self.data_queue = data_queue self.foo_instance = foo_instance # 如果要访问历史数据,就存foo实例 def run(self): while True: # 从队列拿数据,队列为空时会自动阻塞,不用瞎轮询 name, value = self.data_queue.get() # 直接处理新数据 myOtherfunction(value) # 如果需要访问foo的历史数据,记得加锁 if self.foo_instance: with self.foo_instance.var_lock: arg = myfunction() if arg in self.foo_instance.var1: val = self.foo_instance.var1[arg] myOtherfunction(val) # 告诉队列这个任务处理完了(如果用queue.join()等待所有任务完成的话需要) self.data_queue.task_done() # 初始化队列 data_queue = queue.Queue() # 创建并启动线程 foo_thread = foo(data_queue) bar_thread = bar(data_queue, foo_thread) foo_thread.start() bar_thread.start()
这个方案的好处:
- 队列自带线程安全,不用自己操心锁的问题
- 生产者-消费者模式逻辑清晰,foo专心生产,bar专心消费
- 队列的
put()和get()是阻塞式的,避免了无意义的循环轮询,节省资源
最后再提几个小建议
- 别在多线程里直接修改同一个可变对象(字典、列表这类),一定要加同步机制
- 如果需要优雅停止线程,别用
while True死循环,可以加个退出标志,比如self.running = True,外部设置为False后,循环改成while self.running - 要是传递的是不可变对象(整数、字符串),虽然不会有数据竞争,但也要注意可见性,不过Python的GIL在这方面有一定保障,保险起见还是用锁或者队列传递
内容的提问来源于stack exchange,提问作者Jaime
相关产品推荐
相关产品推荐

