Я испытываю странное поведение в Python. Когда я использовал лямбду в качестве цели потока, поведение было непоследовательным.Python: использование лямбда в качестве цели для нитей вызывает странное поведение
Первый пример, как это:
import time,threading
locker= threading.RLock()
def func(obj):
while obj['count']>0:
with locker: print 'thread',obj,id(obj)
obj['count']-= 1
time.sleep(0.1)
with locker: print 'finished',obj,id(obj)
def make_thread1():
threads= []
objs= {}
for i in range(2):
objs[i]= {}
objs[i]['id']= i
objs[i]['count']= (i+2)*2
t= threading.Thread(name='func'+str(i), target=lambda: func(objs[i]))
t.start()
threads.append(t)
return threads,objs
if __name__=='__main__':
threads,objs= make_thread1()
for t in threads:
t.join()
Существовали две модели результатов. Один из них:
thread {'count': 4, 'id': 0} 139911658041704
thread {'count': 6, 'id': 1} 139911658041984
thread {'count': 3, 'id': 0} 139911658041704
thread {'count': 5, 'id': 1} 139911658041984
thread {'count': 4, 'id': 1} 139911658041984
thread {'count': 2, 'id': 0} 139911658041704
thread {'count': 3, 'id': 1} 139911658041984
thread {'count': 1, 'id': 0} 139911658041704
thread {'count': 2, 'id': 1} 139911658041984
finished {'count': 0, 'id': 0} 139911658041704
thread {'count': 1, 'id': 1} 139911658041984
finished {'count': 0, 'id': 1} 139911658041984
Это результат, которого я ожидал. Однако при выполнении этого кода в несколько раз, иногда это приводило так:
thread {'count': 6, 'id': 1} 140389870428800
thread {'count': 5, 'id': 1} 140389870428800
thread {'count': 4, 'id': 1} 140389870428800
thread {'count': 3, 'id': 1} 140389870428800
thread {'count': 2, 'id': 1} 140389870428800
thread {'count': 1, 'id': 1} 140389870428800
finished {'count': 0, 'id': 1} 140389870428800
finished {'count': 0, 'id': 1} 140389870428800
При создании нити, lambda:func(objs[0])
и lambda:func(objs[1])
были определены как целевые функции соответственно, но на самом деле обе целевые функции были lambda:func(objs[1])
(но разные случаи).
Я не могу понять, почему это произошло.
Ну, одна из возможностей заключалась бы в том, что я использовал локальную переменную i
при создании лямбда-функций. Но он должен быть оценен при выполнении t.start()
...? Тогда почему были два шаблона результатов?
Для более расследования, я изменил код без лямбда:
class TObj:
def __init__(self):
self.objs= None
def f(self):
func(self.objs)
def make_thread2():
threads= []
classes= {}
for i in range(2):
classes[i]= TObj()
classes[i].objs= {}
classes[i].objs['id']= i
classes[i].objs['count']= (i+2)*2
t= threading.Thread(name='func'+str(i), target=classes[i].f)
t.start()
threads.append(t)
return threads,classes
if __name__=='__main__':
threads,classes= make_thread2()
for t in threads:
t.join()
Этот код работает отлично:
thread {'count': 4, 'id': 0} 140522771444352
thread {'count': 6, 'id': 1} 140522771445472
thread {'count': 3, 'id': 0} 140522771444352
thread {'count': 5, 'id': 1} 140522771445472
thread {'count': 2, 'id': 0} 140522771444352
thread {'count': 4, 'id': 1} 140522771445472
thread {'count': 1, 'id': 0} 140522771444352
thread {'count': 3, 'id': 1} 140522771445472
finished {'count': 0, 'id': 0} 140522771444352
thread {'count': 2, 'id': 1} 140522771445472
thread {'count': 1, 'id': 1} 140522771445472
finished {'count': 0, 'id': 1} 140522771445472
Я хочу, чтобы понять причины, почему несоответствие первого кода произошло.
Я также хочу знать, как сделать лямбда-функции в петлях безопасно. Если вышеуказанная проблема была вызвана использованием переменной цикла i
в лямбда-функции, нам следует избегать генерации лямбда-функций в циклах. Это было бы неудобно; Я мог бы написать код, как последний, но он был длиннее. Есть ли хорошие идеи?