Python 为什么本地启动的线程不会在每次迭代结束时终止?

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 = [

根据答案中的建议,为了避免RuntimeError:线程只能启动一次,我尝试在创建每个线程的相应对象时启动它们,而不是在代码主体中直接启动它们。最终目的是当主代码的每次迭代完成时,在其中运行的线程也会终止。为了同时启动所有线程,我考虑

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的公式:线程只能启动一次。无论如何,谢谢你的建设性暗示。