Python多线程lambda闭包导致对象方法错误调用问题排查
问题根因
这是Python中闭包晚绑定特性导致的典型问题。
你在列表推导式中定义lambda函数时,lambda并不会立即捕获当前循环的job变量的取值,它只会在实际被调用执行时才会去查找job变量的当前值。
等你启动线程执行lambda时,遍历jobs列表的for循环已经执行完毕,此时job变量最终指向的是列表的最后一个元素,也就是JobB的实例,所以两个线程调用的都是JobB的start()方法。
修复方案
有两种常用的修复方式:
方法1:用lambda默认参数即时绑定变量
Python中函数的默认参数是在函数定义阶段求值的,你可以给lambda加一个默认参数来捕获每次循环的job值:
threads = [threading.Thread(name=job.__str__(), target=lambda j=job: j.start()) for job in jobs]
方法2:用functools.partial绑定参数
partial会在定义阶段就把参数和目标函数绑定,不会出现闭包延迟绑定的问题:
首先导入partial:
from functools import partial
再修改线程创建代码:
threads = [threading.Thread(name=job.__str__(), target=partial(job.start)) for job in jobs]
修改后重新运行代码,两个线程就会分别对应调用JobA和JobB实例的start()方法,输出符合预期。
内容的提问来源于stack exchange,提问作者Garriot Zhang
相关产品推荐
相关产品推荐

