【问题标题】:How do I stop a thread in python which itself is being called inside a loop?如何停止 python 中的线程,该线程本身在循环内被调用?
【发布时间】:2021-12-10 21:54:30
【问题描述】:

根据我在 SO 上找到的其他类似答案,这似乎是一个特别令人困惑的问题。我有类似于以下的代码:

def parentFunction():
    # Other code
    while True:
        var1, var2 = anotherFunction1() # Getting client details after listening on open port
        threading.Thread(target = anotherFunction2, args=(var1, var2)).start()
        childFunction(var1,var2)
        print("PRINT #1: Running in Parent Function") # This only prints once for some reason

def childFunction(var1, var2):
    threading.Timer(10, childFunction, args=(var1,var2)).start()
    print("PRINT #2: Running in child function") # Prints every 10 seconds
    
    # Other code 

    if (someConditionIsMet):
        print("PRINT #3: Exiting") 
        end_process_and_exit_here()

所以基本上,当我运行parentFunction() 时,我会进入一个无休止的循环,每隔 10 秒,我的控制台就会打印 “PRINT #2: Running in child function”。当someConditionIsMet 为真时,我的控制台将打印 "PRINT #3: Exiting" 但随后它不会退出。因此,我的循环将永远持续下去。我不确定它是否相关,但部分代码也有一个 Threading.Lock。

在上面我写过end_process_and_exit_here()的地方,我尝试使用几种方法来杀死一个线程,例如

  1. Raising exceptions and setting flags - 这些假设我已经在循环之外启动了我的线程,所以它没有可比性。
  2. Even this qn about looping threads assumes the thread isnt being looped
  3. Killing using join or stop - stop() 不是我可以访问的选项。 join() 可用,但它不起作用,即在它被调用后,下一个线程 (PRINT #2) 继续打印。
  4. 其他建议使用 signals (1) (2) 的答案也无效。
  5. 在我的代码的不同部分使用 sys.exit() 或 break 也不会导致线程停止。

有什么方法可以让我轻松退出这样的循环线程吗?

注意:我需要使用线程而不是多处理。

【问题讨论】:

  • 您的代码中涉及到相当多的手动操作,您能提取一个minimal reproducible example 吗?无论如何,在无限循环中启动线程似乎是个坏主意。

标签: python multithreading loops terminate


【解决方案1】:

您可以使用python-worker,只需在函数上方添加@worker pip install python-worker

from worker import worker

@worker
def anotherFunction2(var1,var2):
   # your code here
   pass

@worker
def parentFunction():
    # Other code
    while True:
        var1, var2 = anotherFunction1() # Getting client details after listening on open port
        function2Worker = anotherFunction2(var1,var2) # this will automatically run as thread since you put @worker above your function
        childFunction(var1,var2)
        print("PRINT #1: Running in Parent Function") # This only prints once for some reason

def childFunction(var1, var2):
    parentWorker = parentFunction(var1, var2)
    
    # Other code 

    if (someConditionIsMet):
        parentWorker.abort()

【讨论】:

    【解决方案2】:

    所以作为更新,我已经设法解决了这个问题。我所说的另一个答案的问题(如下所示)是 .cancel() 本身似乎只适用于一个计时器线程。但是从问题中可以看出,childFunction()本身调用childFunction()也可以被parentFunction调用,也就是说可能有多个定时器线程。

    对我的具体情况有用的是如下命名我的线程:

    t1 = threading.Timer(10, childFunction, args=(var1,var2,number))
    t1.name = t1.name + "_timer" + str(number)
    t1.start()
    

    此后,我可以通过以下方式取消从此进程创建的所有计时器线程:

    for timerthread in threading.enumerate():
        if timerthread.name.endswith('timer' + str(number)):
            timerthread.cancel()
    

    以下是我使用的导致许多问题的原始方法

    我不确定这是否是一种不好的做法(事实上,我觉得这可能是基于问题中链接的答案,即我们永远不应该“杀死一个线程”)。我敢肯定这不好是有原因的,我很感激有人告诉我原因。但是,最终对我有用的解决方案是使用.cancel()

    所以第一个改变是将你的线程 Timer 分配给一个变量,而不是直接调用它。所以不是threading.Timer(10, childFunction, args=(var1,var2)).start(),应该是

    t = threading.Timer(10, childFunction, args=(var1,var2))
    t.start()
    

    因此,您应该使用t.cancel() 而不是end_process_and_exit_here()。这似乎有效并在进程中停止了所有线程。然而,不好的是它似乎并没有与程序的其他部分一起进行。

    【讨论】:

    • 对变量的赋值有什么变化?因为这实际上与在一行中调用它是一样的(除了现在它将是 2 行)
    • @Matiiss 当我没有将它分配给变量时,下一步 (t.cancel()) 对我不起作用。因此,如果我没有将其分配给变量并将其保留为threading.Timer(10, childFunction, args=(var1,var2)).start(),我不知道如何应用相同的cancel() 解决方案。如果我使用threading.Timer(10, childFunction, args=(var1,var2)).cancel(),除了如上所述在childFunction 方法中不断循环之外,它什么也不做。我确信有更好的方法,但我不知道它们。
    • 不,如果是这样的话,这是要走的路,很简单,如果你以后需要使用一些对象,请参考它
    猜你喜欢
    • 2013-08-03
    • 1970-01-01
    • 2018-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-18
    • 1970-01-01
    相关资源
    最近更新 更多