【问题标题】:Guaranteeing calling to destruction on process termination保证在进程终止时调用销毁
【发布时间】:2018-11-19 19:04:30
【问题描述】:

在阅读了有关该主题的大量数据后,我仍然找不到任何实际解决方案来解决我的问题(可能没有)。

我的问题如下:

在我的项目中,我有多个驱动程序与各种硬件(IO 管理器、可编程负载、电源等)一起工作。

初始化与这些硬件的连接代价高昂(及时),我无法为我们之间的每次通信迭代打开然后关闭连接。

意思是我不能这样做(假设可编程负载实现进入/退出):

start of code...

with programmable_load(args) as program_instance:
     programmable_load_instance.do_something()

rest of code...

所以我选择了不同的解决方案:

class programmable_load():
    def __init__(self):
         self.handler = handler_creator()
    def close_connection(self):
         self.handler.close_connection()
         self.handler = None
    def __del__(self):
         if (self.handler != None):
             self.close_connection()

出于显而易见的原因,我不“相信”析构函数会真正被调用,所以当我想结束我的程序(对于所有驱动程序)时,我显式调用 close_connection()。

当我突然终止进程时会出现问题,例如当我通过调试模式运行并退出调试时。

在这些情况下,进程会在不运行任何析构函数的情况下终止。 我知道操作系统会在此时清除所有未使用的内存,但是有没有办法以有组织的方式清除内存?

如果没有,有没有办法让退出调试功能通过某组功能? python 进程是否知道它得到了一个相当调试的事件,或者它是否将其视为正常终止?

操作系统:Windows

【问题讨论】:

  • 即使在调试会话停止时也要进行清理吗?
  • @AzatIbrakov 我希望在每次进程终止时都使用它,但在调试会话停止时也很好。
  • 不知道调试会话,但看看atexit module
  • 在哪个操作系统上?我认为您可以在 Unix 上使用信号陷阱。
  • 将你的程序一分为二:一个进程将管理物理连接并充当代理,另一个是你的程序逻辑。这样,一旦你的逻辑进程被杀死,comm.经理将能够检测到这一点并优雅地退出。

标签: python python-3.x operating-system


【解决方案1】:

根据this documentation

如果一个进程被TerminateProcess 终止,则该进程的所有线程 进程立即终止没有机会运行额外的 代码。

(强调我的。)这意味着在这种情况下您无能为力。

详细here,信号在 ms-windows 上不能很好地工作。

【讨论】:

    【解决方案2】:

    正如评论中提到的,您可以使用atexit 进行清理。但这只有在进程被要求关闭时才有效(例如 Linux 上的 QUIT 信号),而不仅仅是被杀死(在停止调试会话时很可能是这种情况)。同样,如果您强制关闭计算机(例如长按电源按钮或移除电源),那么它也不会被调用。由于显而易见的原因,没有“解决方案”。你的程序不能指望在突然断电或被强行杀死时被调用。强杀的重点是现在一定要杀掉进程。如果它首先调用了您的清理代码,那么您可以延迟那些违背目的的代码。这就是为什么会有信号要求你的进程停止。这不是特定于 Python 的。同样的概念也适用于所有操作系统。

    奖励(设计建议,而不是解决方案):我认为您仍然可以使用上下文管理器(使用 with)。你的问题不是唯一的。数据库连接通常也会保持更长时间。这是范围的问题。将上下文进一步向上移动到应用程序级别。然后很清楚边界是什么,你不需要任何魔法(你可能也知道@contextmanager 让这变得轻而易举)。

    【讨论】:

    • 问题是即使在使用'with'语句时,连接也不会在进程终止时关闭,还是我错了?
    • @Rohi 为什么你认为它不会被关闭?当然,这取决于您的应用程序。如果在 with 块中调用的其他东西被阻塞并且永远不会返回,那么显然不会起作用。但你可以解决这个问题。
    • 如果您的意思是在强制进程终止时连接不会关闭,那么您是对的。没有任何东西可以让您从第一段中概述的相同过程中做到这一点。在这种情况下,您的进程将被杀死,而您的进程中没有执行任何代码。如果它仍然调用你的代码,那么你仍然可以保持进程活着,这违背了强行杀死你的进程的目的......但是当进程正常终止时将调用清理。
    • 这正是我的问题,导致以普通方式关闭连接非常简单(并且在多个应用程序中完成)。当它不是标准关闭时,我正在寻找解决方案(专门用于“退出调试”目的)。
    • @Rohi 如前所述,由于上述原因,您将找不到使用相同过程的解决方案。您可以使用另一个进程来监视要被杀死的进程,但显然无法访问变量。不知道为什么你需要一个解决方案。你认为强行杀死进程的意义是什么?由操作系统负责清理内存等本地资源,并由远程服务处理连接丢失。我的“奖励”不是解决方案,而是无法使用上下文管理器的反击。
    【解决方案3】:

    我没有正确测试,因为我没有在这里安装wingide,所以我不能保证这会起作用,但是使用setconsolectrlhandler 怎么样?例如,尝试这样的事情:

    import os
    import sys
    import win32api
    
    
    if __name__ == "__main__":
        def callback(sig, func=None):
            print("Exit handler called!")
    
        try:
            win32api.SetConsoleCtrlHandler(callback, True)
        except Exception as e:
            print("Captured exception", e)
            sys.exit(1)
    
        print("Press to quit")
        input()
        print("Bye!")
    

    它将能够处理CTRL+CCTRL+BREAK 信号:

    【讨论】:

    • 它似乎与'ctrl+c'一起工作,这完全等同于进程终止吗?由于某种原因,它不能通过退出调试工作(但我还没有时间正确检查)。非常感谢您的回答!
    猜你喜欢
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    • 2011-12-04
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多