【问题标题】:Python, exit While loop with User Input using multithreading (cntrl+c wont work)Python,使用多线程退出带有用户输入的 While 循环(ctrl+c 不起作用)
【发布时间】:2020-12-24 11:55:51
【问题描述】:

我有一个系统利用函数,monitor(),我想运行它直到用户停止它,方法是输入数字零,exit_collecting()。我不想突然结束程序,因为这会否定后续代码。
我尝试使用多线程运行这两个函数,exit_collecting() 和 monitor(),让 monitor() 一直运行,直到用户通过键入零来停止它,exit_collecting()。

我下面的代码抛出了一堆回溯。我是这方面的新手,任何想法,任何帮助都会很棒。谢谢你。顺便说一句,我最初尝试使用“try with an except for KeyboardInterrupt”,但是当使用 IDLE(Spyder)时,ctrl-c 组合不起作用(分配给“copy”),当我使用 ctrl-c 时不起作用也可以从 Linux 的控制台运行它。

def exit_collecting():
    try:
        val = int(input("Type 0 to exit data collection mode"))
        if val == 0:
            flag = 0
    except:
        print(val,"typo, enter 0 to exit data collection mode")


def monitor():
    import time
    import psutil
    flag = 1
    while flag > 0:
        time.sleep(1)
        print("cpu usuage:",psutil.cpu_percent())
        

from multiprocessing import Process
p1 = Process(target=exit_collecting)
p1.start()
p2 = Process(target=monitor)    
p2.start()
p1.join()
p2.join()

【问题讨论】:

    标签: python while-loop parallel-processing python-multithreading keyboardinterrupt


    【解决方案1】:

    你的多处理版本注定要失败,因为每个进程都有自己的内存空间并且不共享相同的变量flag。它可以通过多处理来完成,但您必须使用使用共享内存的flag 的实现。

    使用线程的解决方案要简单得多,只要您进行一次更改,您的代码就应该可以工作。您忽略了将 flag 声明为全局。这是确保函数exit_collectingmonitor 正在修改同一个变量所必需的。如果没有这些声明,每个函数都在修改 local flag variablle:

    def exit_collecting():
        global flag
        try:
            val = int(input("Type 0 to exit data collection mode"))
            if val == 0:
                flag = 0
        except:
            print(val,"typo, enter 0 to exit data collection mode")
    
    
    def monitor():
        global flag
        import time
        import psutil
        flag = 1
        while flag > 0:
            time.sleep(1)
            print("cpu usuage:",psutil.cpu_percent())
    
    
    from threading import Thread
    p1 = Thread(target=exit_collecting)
    p1.start()
    p2 = Thread(target=monitor)
    p2.start()
    p1.join()
    p2.join()
    

    但也许上面的代码可以通过将monitor 线程作为一个守护进程线程来简化,即当所有非守护线程终止时它会自动终止的线程(如当前所写,看来函数monitor 可以在处理的任何时候终止)。但无论如何,主线程可以执行exit_collecting 正在执行的功能。你现在没有理由不能使用键盘中断(只要你在主线程中等待input 语句):

    def monitor():
        import time
        import psutil
        while True:
            time.sleep(1)
            print("cpu usuage:",psutil.cpu_percent())
    
    
    from threading import Thread
    p = Thread(target=monitor)
    p.daemon = True
    p.start()
    try:
        input("Input enter to halt (or ctrl-C) ...")
    except KeyboardInterrupt:
        pass         
    """
    When the main thread finishes all non-daemon threads have completed
    and therefore the monitor thread will terminate.
    """
    

    更新:允许monitor线程正常终止并允许键盘中断

    我已经简化了逻辑 a,但只是使用一个简单的全局标志 terminate_flag,最初是 False,它只能由 monitor 线程读取,因此不必将其显式声明为全局:

    terminate_flag = False
    
    def monitor():
        import time
        import psutil
        while not terminate_flag:
            time.sleep(1)
            print("cpu usuage:", psutil.cpu_percent())
    
    
    from threading import Thread
    
    p = Thread(target=monitor)
    p.start()
    try:
        input('Hit enter or ctrl-c to terminate ...')
    except KeyboardInterrupt:
        pass
    terminate_flag = True # tell monitor it is time to terminate
    p.join() # wait for monitor to gracefully terminate
    

    【讨论】:

    • 谢谢你,特别是清晰的措辞。第一个解决方案效果很好。在我的代码中,还有一些报告在 WHILE 循环关闭后保存到 csv 中。第一个解决方案顺利进行,但第二个解决方案似乎没有发生(不确定它是否挂起)。对我来说,一个“可行的解决方案”太棒了!
    • 为了澄清,我可以添加一些东西,以便当 WHILE 循环被中断时,它会在继续执行剩余代码之前完成该块?
    • 如果因为您是守护线程而终止并且所有非守护线程都已终止,那么您无法控制终止。因此,您必须像第一个代码示例一样通过测试flag 值来使用终止。但是您仍然可以摆脱 exit_collecting 线程并在主线程中执行该逻辑,以便您可以处理 ctrl-c 键盘中断。
    • 再次感谢您的澄清。我尝试使用主线程解决它并更改 try/except 中的标志,但我对此很陌生,它没有崩溃但似乎没有工作。第一个解决方案效果很好,即使对于新手 tnx 也是如此。
    • 我添加了一个新版本。
    猜你喜欢
    • 2014-04-12
    • 2012-12-22
    • 2018-07-25
    • 2017-07-03
    • 1970-01-01
    • 2018-08-18
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    相关资源
    最近更新 更多