Python 为什么本地启动的线程不会在每次迭代结束时终止?
根据答案中的建议,为了避免RuntimeError:线程只能启动一次,我尝试在创建每个线程的相应对象时启动它们,而不是在代码主体中直接启动它们。最终目的是当主代码的每次迭代完成时,在其中运行的线程也会终止。为了同时启动所有线程,我考虑Python 为什么本地启动的线程不会在每次迭代结束时终止?,python,multithreading,Python,Multithreading,根据答案中的建议,为了避免RuntimeError:线程只能启动一次,我尝试在创建每个线程的相应对象时启动它们,而不是在代码主体中直接启动它们。最终目的是当主代码的每次迭代完成时,在其中运行的线程也会终止。为了同时启动所有线程,我考虑 for obj in objects: obj.launch_obj(q) 在下面的代码片段中 import random from threading import Thread from queue import Queue objects = [
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-如何修复第一个代码段的行为,其中线程在循环中启动,从而使每个迭代的线程在该迭代结束时终止
谢谢你误诊了你的错误。这与线程未终止无关 在rangeepochs循环中for历元的每次迭代中,对象列表中的对象越来越多,并且在每次迭代中,为列表中的每个对象启动一个新线程。在以后的迭代中意外的输出并不是由于某些原因仍然存在的旧线程。这是从新的线程你永远不应该开始
尝试将对象添加到_uinit_uu中的全局注册表是一个坏主意,会导致类似这样的错误。明确管理您的数据结构-它使跟踪哪些对象与哪些代码相关变得更加容易,有助于避免对象永远存在,并且使使用多个数据结构变得更加容易。您已经误诊了错误。这与线程未终止无关 在rangeepochs循环中for历元的每次迭代中,对象列表中的对象越来越多,并且在每次迭代中,为列表中的每个对象启动一个新线程。在以后的迭代中意外的输出并不是由于某些原因仍然存在的旧线程。这是从新的线程你永远不应该开始
尝试将对象添加到_uinit_uu中的全局注册表是一个坏主意,会导致类似这样的错误。明确管理您的数据结构-它使跟踪哪些对象与哪些代码相关变得更容易,有助于避免对象永远存在,并且使使用多个数据结构变得更容易。没有区别,问题是如何跟踪对象 对于每个迭代,您只对在该迭代中创建的对象感兴趣,是吗?因此,没有理由全局定义该列表。因此,将objects=[]移动到for循环内部。这样,每次迭代都会很好,而且是空的 当然,除非您将对象传递给构造函数,否则类不能将自己添加到构造函数中的列表中。但老实说,你应该避免这样做。现在我们有了这个: 对于范围内的历元历元: printiteration:{0}.formatepoch+1 对象=[] ... objects.appendMy_类 objects.appendMy_类 ... 重置用户id 一般来说,你需要考虑你的逻辑应该在哪里。类应该自行处理,不必依赖于任何外部对象或方法。为此,“generate_id”和“reset_id”方法也应该移到类定义内部。也许是这样 随机输入 从线程导入线程 从队列导入队列 将My_类对象分类: ID=0 @类方法 def生成idcls: cls.ID+=1 返回cls.ID @类方法 def reset_idcls: cls.ID=0 定义初始自我: self.id=My_class.generate_id def启动_objself,q: thread=Threadtarget=do,args=q,self.id, thread.start 回位螺纹 纪元=4 成功=[0代表rangeepochs中的x] def doq,id: 温度=q.get 如果random.random>0.5: 温度+=1 打印\t对象:{0}->temp:{1}.formatid,temp 返回q.puttemp 对于范围内的历元历元: printiteration:{0}.formatepoch+1 对象=[] 线程=[] 温度=0 q=队列 q、 puttemp objects.appendMy_类 objects.appendMy_类 对于对象中的obj: threads.appendobj.launch_objq 对于线程中的线程: 线程连接 温度=q.gettemp 成功[时代]=temp 我的\u class.reset\u id
现在,对于某些用例,让类将自己添加到对象列表是完全合理的。一种情况可能是它需要跟踪它的兄弟姐妹。但是,您需要明确地说明这一点,以便未来的您或其他开发人员很容易看到正在发生的事情。没有区别,问题是如何 eep跟踪您的对象 对于每个迭代,您只对在该迭代中创建的对象感兴趣,是吗?因此,没有理由全局定义该列表。因此,将objects=[]移动到for循环内部。这样,每次迭代都会很好,而且是空的 当然,除非您将对象传递给构造函数,否则类不能将自己添加到构造函数中的列表中。但老实说,你应该避免这样做。现在我们有了这个: 对于范围内的历元历元: printiteration:{0}.formatepoch+1 对象=[] ... objects.appendMy_类 objects.appendMy_类 ... 重置用户id 一般来说,你需要考虑你的逻辑应该在哪里。类应该自行处理,不必依赖于任何外部对象或方法。为此,“generate_id”和“reset_id”方法也应该移到类定义内部。也许是这样 随机输入 从线程导入线程 从队列导入队列 将My_类对象分类: ID=0 @类方法 def生成idcls: cls.ID+=1 返回cls.ID @类方法 def reset_idcls: cls.ID=0 定义初始自我: self.id=My_class.generate_id def启动_objself,q: thread=Threadtarget=do,args=q,self.id, thread.start 回位螺纹 纪元=4 成功=[0代表rangeepochs中的x] def doq,id: 温度=q.get 如果random.random>0.5: 温度+=1 打印\t对象:{0}->temp:{1}.formatid,temp 返回q.puttemp 对于范围内的历元历元: printiteration:{0}.formatepoch+1 对象=[] 线程=[] 温度=0 q=队列 q、 puttemp objects.appendMy_类 objects.appendMy_类 对于对象中的obj: threads.appendobj.launch_objq 对于线程中的线程: 线程连接 温度=q.gettemp 成功[时代]=temp 我的\u class.reset\u id
现在,对于某些用例,让类将自己添加到对象列表是完全合理的。一种情况可能是它需要跟踪它的兄弟姐妹。但是,您需要明确说明这一点,以便将来的您或其他开发人员能够很容易地看到正在发生的事情。但是,如果您指出了问题所在,那么连续启动线程也会有问题,因为在这两种情况下,代码中这些列表的管理方式是相同的。但是,只有在循环中启动线程时,此问题才会出现。@robotist:手动启动时,并不是每个对象都启动线程。您只为最近一次迭代中创建的对象启动了线程。我明白了。你认为以下简单的变通方法是否可行?在每次迭代中,我选择对象的最后两项,即最近的两项,并只为它们启动线程。@机器人专家:更改填充和管理对象列表的方式将使事情更易于管理。我尝试过这一项,但这个公式是唯一一个我能够设法摆脱RuntimeError的公式:线程只能启动一次。无论如何,感谢您的建设性提示。但是如果您指出的是问题所在,那么连续启动线程也会有问题,因为在这两种情况下,代码中这些列表的管理方式是相同的。但是,只有在循环中启动线程时,此问题才会出现。@robotist:手动启动时,并不是每个对象都启动线程。您只为最近一次迭代中创建的对象启动了线程。我明白了。你认为以下简单的变通方法是否可行?在每次迭代中,我选择对象的最后两项,即最近的两项,并只为它们启动线程。@机器人专家:更改填充和管理对象列表的方式将使事情更易于管理。我尝试过这一项,但这个公式是唯一一个我能够设法摆脱RuntimeError的公式:线程只能启动一次。无论如何,谢谢你的建设性暗示。