首页 > 解决方案 > Python,使用多线程退出带有用户输入的 While 循环(cntrl+c 不起作用)

问题描述

我有一个系统利用函数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()

标签: pythonwhile-loopparallel-processingpython-multithreadingkeyboardinterrupt

解决方案


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

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

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

推荐阅读