У меня есть функция использования системы, 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()
Ваша версия с многопроцессорностью обречена на провал, так как каждый процесс имеет свое пространство памяти и не использует одну и ту же переменную 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
Для пояснения, есть ли что-то, что я могу добавить, чтобы при прерывании цикла WHILE он заканчивал блок, прежде чем перейти к оставшемуся коду?
Если вы завершаетесь, потому что вы являетесь потоком демона, а все потоки, не являющиеся демонами, завершены, то вы не можете контролировать свое завершение. Таким образом, вы должны использовать терминацию, проверяя значение flag
, как в первом примере кода. Но вы все еще можете избавиться от потока exit_collecting
и выполнить эту логику в основном потоке, чтобы вы могли обрабатывать прерывание клавиатуры ctrl-c.
Еще раз спасибо за разъяснения. Я попытался решить это, используя основной поток и изменив флаг в try/кроме того, что последовало, но я довольно новичок в этом, он не сбой, но, похоже, не работает. Первое решение работало хорошо, и даже новичку было сложно следовать tnx.
Я добавил новую версию.
Если он выходит из блока while в середине потока, поэтому запись результатов в файл становится неполной, я ценю образование.
Я не уверен, что понял ваш последний комментарий. monitor
нельзя прерывать. Он будет работать до тех пор, пока не обнаружит, что terminate_flag
является True
, а затем выйдет из цикла while и вернется, и поток завершится.
Извините, я беру это назад, когда я изменил код, я должен что-то нажать и, по-видимому, сместил строку кода из блока, это работает хорошо. Еще раз спасибо всем за помощь.
Спасибо, особенно за четкую формулировку. Первое решение сработало хорошо. В моем коде делается еще несколько отчетов, которые сохраняются в csv после закрытия цикла WHILE. Это происходит плавно с первым решением, но, похоже, не происходит со вторым (не уверен, что оно зависает). Одно "рабочее решение" для меня просто фантастика!