我有一个运行(嵌套)循环的Python程序,该循环将运行相当长的时间,我希望用户只需按 p 即可暂停和/或中止它分别是 c 。
我正在IPython控制台中运行它,所以我实际上没有访问msvctr.getch
的权限,我想想要使其保持平台独立。
很显然,input()
块是我不想要的。因此,我尝试了线程化,该线程可按预期使用,但按 CTRL C 时,线程不会停止。这很可能是因为线程阻塞导致未执行任何停止线程的合法方法(atexit,全局变量或lambda stop_thread
)。
import threading
import queue
q = queue.SimpleQueue()
stop_thread = False
def handle_input(q, stopped):
s = ''
while not stopped():
s = input()
q.put(s)
thread = threading.Thread(target=handle_input,
args=[q, lambda: stop_thread])
thread.start()
for i in range(very_long_time):
#Do something time consuming
if not q.empty():
s = q.get_nowait()
if 'p' in s:
print('Paused...', end='\r')
s = s.replace('p', '')
while True:
if not q.empty():
s += q.get_nowait()
if 'p' in s or 'c' in s:
s = s.replace('p', '')
break
time.sleep(0.5)
if 'c' in s:
print('\rAborted training loop...' + ' '*50, end='\r')
s = s.replace('c', '')
stop_thread = True
# Another method of stopping the thread
# thread.__getattribute__('_tstate_lock').release()
# thread._stop()
# thread.join()
break
这原则上可行,但在中断时会中断。
该线程似乎没有停止,这在同一个控制台中再次运行该线程时会造成问题,因为它甚至不要求用户输入。
此外,这会打印我的'c'或'p'和换行符,因为IPython不允许所有ANSI转义,所以我无法摆脱它。
我的方法是否可以解决,或更完善的替代方法?
答案 0 :(得分:1)
您可以尝试使用keyboard
module,它(除其他外)使您可以将事件挂钩绑定到键盘按下。
在这种情况下,我将创建一组全局变量/标志(例如paused
和abort
),最初设置为False
,然后为 p 和 c 分别切换它们:
paused = False
abort = False
def toggle_paused():
global paused
paused = not paused
def trigger_abort():
abort = True
keyboard.add_hotkey('p', toggle_paused())
keyboard.add_hotkey('c', trigger_abort())
然后更改循环以在每次迭代中检查paused
和abort
(假设每次迭代都相当快)。您已经在做的工作或多或少会起作用-只需删除已经设置的队列和线程处理的东西(无论如何,IIRC keyboard
的事件都会在自己的线程上运行),将{ {1}}条件,并将条件分别更改为if
和if paused:
。
您还可以在代码的其余部分加上寻找if abort:
或pause
标志的内容,以便您的程序可以在方便的时候优雅地暂停或退出。您还可以扩展abort
和toggle_paused()
来完成您需要的任何操作(例如让trigger_abort()
打印trigger_abort()
或其他内容。
尽管,正如@Tomerikoo在评论中建议的那样,如果可以使用程序的设计方式,则使用"Trying to abort program (kill me if I'm not done in 5 seconds)"
选项创建威胁是最佳答案。如果这是您的所有程序都,那么使用守护程序线程将不起作用,因为您的程序会立即退出,但是如果这是后台操作,则可以使用守护程序线程来放入,它不会在后台影响用户的其他体验。