如何在Python3读取流时正确终止线程

7

我正在使用线程从流(/dev/tty1)中读取字符串,同时在主循环中处理其他事情。当按下CTRL-C时,我希望该线程能够和主程序一起终止。

   from threading import Thread

   class myReader(Thread):
      def run(self):
         with open('/dev/tty1', encoding='ascii') as myStream:
            for myString in myStream:
               print(myString)
      def quit(self):
         pass # stop reading, close stream, terminate the thread

   myReader = Reader()
   myReader.start()
   while(True):
      try:
         pass # do lots of stuff
      KeyboardInterrupt:
         myReader.quit()
         raise

通常的解决方案 - 在run()循环内部使用布尔变量 - 在这里不起作用。有什么推荐的方法来处理这个问题?

我可以设置守护进程标志,但那样我就无法使用quit()方法了,而这个方法可能以后会证明非常有价值(用于一些清理工作)。有什么想法吗?

2个回答

6
据我所知,Python 3中没有内置机制(就像Python 2一样)来实现这一点。您是否尝试过经过验证的Python 2方法,使用PyThreadState_SetAsyncExc,在此处 这里记录下来,或者使用跟踪方式替代
以下是稍微改进的PyThreadState_SetAsyncExc方法版本:
import threading
import inspect
import ctypes 
 
def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble, 
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")
 
def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)

我发现我需要使用ctypes.c_long(tid),否则我会得到res == 0 - faulty
我更新了代码,使用ctypes.c_long(tid)代替了仅有的 tid,现在它可以按预期工作了。完全工作的代码片段。实际上,异步异常可能不会中断对文件对象的阻塞读取,因此变成了等同于可以从主线程设置的手动检查标志的解决方案--这是一个更干净的解决方案。如果需要中断阻塞读取,则应该使用非阻塞I/O。 - Irfy

4

将您的线程设置为守护进程线程(daemon thread)。当所有非守护线程结束时,程序退出。因此,当Ctrl-C传递给您的程序并且主线程退出时,无需显式地终止读取器。

    myReader = Reader()
    myReader.daemon = True
    myReader.start()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接