python - try-except-finally 代码在线程应用程序中无法按预期工作

标签 python multithreading sockets zeromq try-finally

如果线程/进程被杀死,执行突然停止是有道理的

为什么我在终端窗口点击[X]正常退出主程序时不执行清理代码?


我仍在学习多线程应用程序的来龙去脉,我认为我的问题来自不了解 Python 如何处理杀死后台线程。

问题:

  1. 为什么我的 finally: block 不会一直执行?
  2. 什么时候 finally: block 不会执行?
  3. 当线程被终止时,线程内的代码执行会发生什么情况?
  4. 退出主进程时守护进程/非守护进程线程会发生什么?

详情:

我正在尝试使用 ZMQ 套接字编写一个多线程程序,该套接字(除其他外)将内容写入日志文件。我希望日志记录线程在它结束之前无条件地执行一些消息传递和清理,但大多数时候不会。

下面的函数在后台线程中启动一个无限循环,并返回一个用于通信的zmq.PAIR 套接字。它开始的循环监听套接字,写入该套接字的任何内容都会写入文件。该循环还(应该)传输返回 诊断消息,例如“我现在开始记录!”,“糟糕,出现错误!” “我现在退出”。所以主程序可以密切关注它。

main 程序使用这种模式生成一些线程来监视/控制不同的点点滴滴。它轮询几个 ZMQ 套接字(连接到 STDIN 和串行端口)以获取消息,并将其中一些转发到连接到文件的套接字。

但现在我卡住了。 main 程序的路由和控制逻辑工作正常。 get_logfile_sock 的文件写入工作正常,正常的异常处理按预期工作。 但是“我现在退出”代码不会在主程序中的线程被终止时执行,或者当我完全停止主程序时。

示例:

def get_logfile_sock(context, file_name):
    """
    Returns a ZMQ socket. Anything written to the socket gets appended to the a specified file. The socket will send diagnostic messages about file opening/closing and any exceptions encountered. 

    """

    def log_file_loop(socket):
        """
        Read characters from `socket` and write them to a file. Send back diagnostic and exception information.
        """
        try:
            socket.send("Starting Log File {}".format(file_name))
            with open(file_name, "a+") as fh:
                # File must start with a timestamp of when it was opened
                fh.write('[{}]'.format(get_timestamp()))
                # Write all strings/bytes to the file
                while True:
                    message = socket.recv()

                    fh.write(message)
                    fh.flush()

                    # Un-comment this line to demonstrate that the except: and finally: blocks both get executed when there's an error in the loop
                    # raise SystemExit

        except Exception as e:
            # This works fine when/if there's an exception in the loop
            socket.send("::".join(['FATALERROR', e.__class__.__name__, e.message]))
        finally:
            # This works fine if there's an exception raised in the loop
            # Why doesn't this get executed when my program exits? Isn't that just the main program raising SystemExit? 

            # Additional cleanup code goes here
            socket.send("Closing socket to log file {}".format(file_name))
            socket.close()


    # Make a socket pair for communication with the loop thread
    basename = os.path.basename(file_name).replace(":", "").replace(" ", "_").replace(".", "")
    SOCKNAME = 'inproc://logfile-{}'.format(basename)
    writer = context.socket(zmq.PAIR)
    reader = context.socket(zmq.PAIR)
    writer.bind(SOCKNAME)
    reader.connect(SOCKNAME)

    # Start the loop function in a separate thread
    thread = threading.Thread(target=log_file_loop, args=[writer])
    thread.daemon = True  # is this the right thing to do?
    thread.start()

    # Return a socket endpoint to the thread
    return reader

最佳答案

doesn't execute when the thread is killed

不要杀死线程。请他们友好地退出,然后 join 他们。考虑传入 Condition供他们检查。

长答案:执行 kill 将导致线程退出,但不能保证它完成任何特定的 block ,您不应该期望您的系统之后有良好的行为。不过,在使用 multiprocessing 时这样做可能更安全一些。

关于python - try-except-finally 代码在线程应用程序中无法按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26148987/

相关文章:

linux - 将 AF_PACKET 套接字绑定(bind)到所有接口(interface)

python - 如何以与制造商无关的方式在 Python 中以编程方式获取支持 Google 云打印的打印机的托盘列表?

python - 在不重新启动主脚本的情况下加载/重新加载 Python 中的部分代码

c# - SynchronizationContext Send() 应该是同一个线程?

c# - 从单独的后台工作人员更改表格

c# - 为什么我的 C# 服务中会出现此 SocketException?

python - 合并多个 KML 文件

c# - C# 和 Python 之间有哪些核心概念差异?

java - 在这种情况下我需要使用线程安全映射吗?

java - 如何在单台 linux 机器上创建 50000 个 tcp 连接?