根据这里答案中的建议,为了摆脱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-如何修复第一个代码段(其中线程在循环中启动(的行为,使每个迭代的线程在该迭代结束时终止?
感谢
您错误诊断了您的错误。这与线程未终止无关。
在for epoch in range(epochs)
循环的每一次迭代中,objects
列表中都会有越来越多的对象,在每次迭代中,您会为列表中的每个对象启动一个新线程。稍后迭代中的意外输出不是来自由于某种原因仍在延迟的旧线程。它来自新的线程,而你本不应该开始。
尝试将对象添加到__init__
中的全局注册表是一个坏主意,它会导致这样的错误。显式管理您的数据结构-这使跟踪哪些对象与哪些代码相关变得更加容易,有助于避免永远保留对象,并使使用多个数据结构变得更加容易。
没有区别,问题是如何跟踪对象。
对于每次迭代,您只对该迭代中创建的对象感兴趣,是吗?那么,没有理由对该列表进行全局定义。因此,将objects = []
移动到for循环内部。这样,对于每次迭代,它都会很好并且是空的。
当然,类不能将自己添加到构造函数中的列表中(除非将objects
传递给构造函数(。但说实话,你应该避免那样做。现在我们有了这个:
for epoch in range(epochs):
print("iteration: {0}".format(epoch+1))
objects = []
...
objects.append(My_class())
objects.append(My_class())
...
reset_id()
一般来说,你需要考虑你的逻辑应该在哪里。一个类应该自己处理,不必依赖任何外部对象或方法。为此,"generate_id"one_answers"reset_id"方法也应该移到类定义内部。也许是这样?
import random
from threading import Thread
from queue import Queue
class My_class(object):
ID = 0
@classmethod
def generate_id(cls):
cls.ID +=1
return cls.ID
@classmethod
def reset_id(cls):
cls.ID = 0
def __init__(self):
self.id = My_class.generate_id()
def launch_obj(self, q):
thread = Thread(target=do, args=(q,self.id,))
thread.start()
return thread
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))
objects = []
threads = []
temp = 0
q = Queue()
q.put(temp)
objects.append(My_class())
objects.append(My_class())
for obj in objects:
threads.append(obj.launch_obj(q))
for thread in threads:
thread.join()
temp = q.get(temp)
success[epoch] = temp
My_class.reset_id()
现在,让类将自己添加到某些用例的对象列表中是完全合理的。一种情况可能是它需要跟踪它的兄弟姐妹。但你们需要明确这一点,这样你们或其他开发人员就可以很容易地看到正在发生的事情。