python - 为什么本地启动的线程不会在每次迭代结束时终止?
问题描述
根据此处答案中的建议,为了摆脱RuntimeError: threads can only be started once
,我尝试在创建相应对象时启动每个线程(而不是直接在代码主体中启动它们)。最终目的是当主代码的每次迭代完成时,其中运行的线程也终止。要同时启动所有线程,我考虑
for obj in objects:
obj.launch_obj(q)
在以下代码段中。
import random
from threading import Thread
from queue import Queue
objects = []
threads = []
def generate_id():
My_class.ID +=1
return My_class.ID
def reset_id():
My_class.ID = 0
class My_class(object):
ID = 0
def __init__(self):
self.id = generate_id()
objects.append(self)
def launch_obj(self, q):
thread = Thread(target=do, args=(q,self.id,))
threads.append(thread)
thread.start()
epochs = 4
success = [0 for x in range(epochs)]
def do(q, id):
temp = q.get()
if random.random() > 0.5:
temp += 1
print("\t object: {0} -> temp: {1}".format(id, temp))
return q.put(temp)
for epoch in range(epochs):
print("iteration: {0}".format(epoch+1))
temp = 0
q = Queue()
q.put(temp)
obj1 = My_class()
obj2 = My_class()
for obj in objects:
obj.launch_obj(q)
for thread in threads:
thread.join()
temp = q.get(temp)
success[epoch] = temp
reset_id()
但是,输出看起来像
iteration: 1
object: 1 -> temp: 0
object: 2 -> temp: 0
iteration: 2
object: 1 -> temp: 0
object: 2 -> temp: 0
object: 1 -> temp: 1
object: 2 -> temp: 2
iteration: 3
object: 1 -> temp: 0
object: 2 -> temp: 1
object: 1 -> temp: 2
object: 2 -> temp: 3
object: 1 -> temp: 3
object: 2 -> temp: 3
iteration: 4
object: 1 -> temp: 0
object: 2 -> temp: 1
object: 1 -> temp: 1
object: 2 -> temp: 2
object: 1 -> temp: 3
object: 2 -> temp: 4
object: 1 -> temp: 4
object: 2 -> temp: 4
其中每次迭代的线程在迭代结束时不会被杀死。另一方面,如果我单独启动线程,比如
obj1.launch_obj(q)
obj2.launch_obj(q)
然后,输出的形式显然变得与我期望的相似。
iteration: 1
object: 1 -> temp: 0
object: 2 -> temp: 1
iteration: 2
object: 1 -> temp: 1
object: 2 -> temp: 1
iteration: 3
object: 1 -> temp: 1
object: 2 -> temp: 1
iteration: 4
object: 1 -> temp: 1
object: 2 -> temp: 1
所以,我有以下两个相关的问题。
1-在循环中启动线程与按顺序执行线程有什么区别?
2-如何修复第一个片段的行为(其中线程在循环中启动),以便每次迭代的线程在该迭代结束时终止?
谢谢
解决方案
你误诊了你的错误。这与线程不终止无关。
您的objects
列表在循环的每次迭代中都会获得越来越多的对象,并且在每次迭代中,您都会为列表中的每个对象for epoch in range(epochs)
启动一个新线程。后期迭代中的意外输出不是来自由于某种原因仍然挥之不去的旧线程。它来自你不应该开始的新线程。
尝试将对象添加到全局注册表__init__
是一个坏主意,会导致这样的错误。明确地管理你的数据结构——它可以更容易地跟踪哪些对象与哪些代码相关,它有助于避免永远保留对象,并且它使使用多个数据结构变得更加容易。