Python, выход из цикла While с пользовательским вводом с использованием многопоточности (cntrl+c не работает)

У меня есть функция использования системы, monitor(), которую я хочу запускать до тех пор, пока пользователь не остановит ее, введя число Zero, exit_collecting(). Я не хочу резко завершать программу, потому что это сведет на нет последующий код.
Я попытался запустить две функции, используя многопоточность, exit_collecting() и monitor(), позволив монитору() работать до тех пор, пока пользователь не остановит его, набрав Zero, exit_collecting().

Мой код ниже выдал кучу трассировок. Я новичок в этом, любые идеи, любая помощь будет здорово. Спасибо. Кстати, изначально я пытался использовать «попробовать с исключением KeyboardInterrupt», но при использовании IDLE (Spyder) комбинация ctrl-c не работает (она назначена «копировать»), ctrl-c не работает, когда я запускал его с консоли и в линуксе.

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 есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
698
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваша версия с многопроцессорностью обречена на провал, так как каждый процесс имеет свое пространство памяти и не использует одну и ту же переменную flag. Это можно сделать с помощью многопроцессорной обработки, но вы должны использовать реализацию flag, использующую разделяемую память.

Решение, использующее многопоточность, намного проще, и ваш код должен работать, если вы сделаете только одно изменение. Вы забыли объявить flag глобальным. Это необходимо, чтобы гарантировать, что функции exit_collecting и monitor изменяют одну и ту же переменную. Без этих объявлений каждая функция изменяет локальную переменную 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 изящно завершиться и разрешить прерывание клавиатуры

Я упростил логику, но только для того, чтобы использовать простой глобальный флаг 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

Спасибо, особенно за четкую формулировку. Первое решение сработало хорошо. В моем коде делается еще несколько отчетов, которые сохраняются в csv после закрытия цикла WHILE. Это происходит плавно с первым решением, но, похоже, не происходит со вторым (не уверен, что оно зависает). Одно "рабочее решение" для меня просто фантастика!

eli 24.12.2020 14:58

Для пояснения, есть ли что-то, что я могу добавить, чтобы при прерывании цикла WHILE он заканчивал блок, прежде чем перейти к оставшемуся коду?

eli 24.12.2020 14:59

Если вы завершаетесь, потому что вы являетесь потоком демона, а все потоки, не являющиеся демонами, завершены, то вы не можете контролировать свое завершение. Таким образом, вы должны использовать терминацию, проверяя значение flag, как в первом примере кода. Но вы все еще можете избавиться от потока exit_collecting и выполнить эту логику в основном потоке, чтобы вы могли обрабатывать прерывание клавиатуры ctrl-c.

Booboo 24.12.2020 15:08

Еще раз спасибо за разъяснения. Я попытался решить это, используя основной поток и изменив флаг в try/кроме того, что последовало, но я довольно новичок в этом, он не сбой, но, похоже, не работает. Первое решение работало хорошо, и даже новичку было сложно следовать tnx.

eli 24.12.2020 19:14

Я добавил новую версию.

Booboo 24.12.2020 19:30

Если он выходит из блока while в середине потока, поэтому запись результатов в файл становится неполной, я ценю образование.

eli 24.12.2020 20:18

Я не уверен, что понял ваш последний комментарий. monitor нельзя прерывать. Он будет работать до тех пор, пока не обнаружит, что terminate_flag является True, а затем выйдет из цикла while и вернется, и поток завершится.

Booboo 24.12.2020 20:41

Извините, я беру это назад, когда я изменил код, я должен что-то нажать и, по-видимому, сместил строку кода из блока, это работает хорошо. Еще раз спасибо всем за помощь.

eli 24.12.2020 20:58

Другие вопросы по теме