为什么不能从 Kivy 终止这个 Python 多处理进程? [英] Why can't this Python multiprocessing process be terminated from Kivy?

查看:28
本文介绍了为什么不能从 Kivy 终止这个 Python 多处理进程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从 Kivy 应用程序中运行 django 开发服务器.到目前为止,这确实很有效.

I'm trying to run a django development server from within a Kivy application. This did work out quite well so far.

现在我想允许用户在服务器运行时继续使用该程序.我的想法是为 httpd.serve_forever() 创建一个 multiprocessing.Process 以避免主程序完全锁定.做得很好.这是我的 internal_django 模块中的代码:

Now i want to allow the user to continue working with the program while the server is running. My idea was to create a multiprocessing.Process for the httpd.serve_forever() to avoid a complete lock of the main program. Did work well. This is the code in my internal_django module:

import multiprocessing
import os
import time

from wsgiref.simple_server import make_server

def django_wsgi_application():

    PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
    settings_module = "djangosettings"#%s.djangosettings" % PROJECT_ROOT.split(os.sep)[-1]

    os.environ.update({"DJANGO_SETTINGS_MODULE":settings_module})

    from django.core.wsgi import get_wsgi_application
    application = get_wsgi_application()

    return application


class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]


class DjangoServer():
    __metaclass__ = Singleton

    def start(self):
        self.httpd = make_server('', 8000, django_wsgi_application())
        self.server = multiprocessing.Process(target=self.httpd.serve_forever)
        self.server.start()
        print "Now serving on port 8000..."
        print "Server Process PID = %s" %self.server.pid

    def stop(self):
        print("shutdown initiated")
        print "Server Process PID = %s" %self.server.pid
        while self.server.is_alive():
            self.server.terminate()
            print("Server should have shut down")
            time.sleep(1)
        print("Server is_alive: %s" %self.server.is_alive())
        self.server.join()
        print("server process joined")



if __name__ == "__main__":
    server = DjangoServer()
    server.start()
    time.sleep(3)
    server.stop()

当我运行这段代码时,一切都按预期工作.这是控制台中显示的内容:

When i run this code, everything works as expected. This is what is being put out in the console:

Now serving on port 8000...
Server Process PID = 1406
shutdown initiated
Server Process PID = 1406
Server should have shut down
Server is_alive: False
server process joined

下一步是提供一种从 Kivy 应用程序中停止服务器的方法.为此,我只想像以前一样使用我的 DjangoServer 类:

Next step was to provide a way to stop the server from within the Kivy application. For that i just wanted to use my DjangoServer class as i did before:

from internal_django import DjangoServer

class StartScreen(Screen): 
    def start_server(self):
        server = DjangoServer()
        server.start()


class StopScreen(Screen):  
    def stop_server(self):
        server = DjangoServer()
        server.stop()

但是这样做时,进程一旦开始就永远不会退出.我的第一个想法是 Singleton 没有按预期工作,我试图退出错误的过程.但正如您在输出中看到的,PID 是相同的.服务器收到终止命令,但只是继续工作.这是控制台的样子:

But when doing so, the process once started never quits. My first idea was that the Singleton did not work as expected, and that i try to quit the wrong process. but as you can see in the output, the PID's are identical. The server receives the terminate command, but just continues to work. This is what the console looks like:

Now serving on port 8000...
Server Process PID = 1406
shutdown initiated
Server Process PID = 1406
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down
Server should have shut down

(and so on, until i manually kill the server process)

我是否以完全错误的方式使用多处理?Kivy 是否以某种方式干扰了该过程?

Am i using multiprocessing in a completely wrong way? Is Kivy somehow interfering with the process?

推荐答案

我觉得这里的问题可能有两个:

I think the problems here might be two:

  1. 信号处理程序正在拦截 Process.terminate() 发送的 TERM 请求并忽略它.要验证这一点,只需在新进程中使用 signal.getsignal(signal.SIGTERM) 并打印结果.为了避免此类问题,您可以使用 signal.signal(signal.SIGTERM, signal.SIG_DFL) 重置默认行为,但请记住,SIGTERM 可能被框架静音是有原因的(我对 Django 也不熟悉Kivy 也不行).

  1. A signal handler is intercepting the TERM request sent by Process.terminate() and ignores it. To verify that simply use the signal.getsignal(signal.SIGTERM) from within the new process and print the results. To circumvent such issue you can reset the default behavior with signal.signal(signal.SIGTERM, signal.SIG_DFL), nevertheless keep in mind that there might be a reason why SIGTERM is silenced by the frameworks (I'm not familiar neither with Django nor with Kivy).

如果您使用的是 Python 2,您必须考虑如果解释器在来自线程库(锁、信号量..)的同步原语或本机 C 调用上被阻塞,则解释器不会处理信号.在这些情况下,serve_forever() 函数可能会失效(使用源的力量!).快速检查可能是尝试在 Python 3 上运行代码并查看它是否有效.

If you're using Python 2 you must consider that the interpreter does not process signals if it's blocked on a synchronization primitive from threading library (Locks, Semaphores..) or on a native C call. The serve_forever() function might fall in these cases (use the force of the source!). Quick check could be trying to run the code on Python 3 and see whether it works or not.

一个快速而肮脏的解决方案是等待一小段时间,如果进程仍然存在,则发送 SIGKILL.

A quick and dirty solution consists in waiting a small amount of time and send a SIGKILL if the process is still alive.

import os
import signal

process.terminate()
process.join(1)

if process.is_alive() and os.name != 'nt':
    try:
        os.kill(process.pid, signal.SIGKILL)
        process.join()
    except OSError:
        return  # process might have died while checking it

在 Windows 上,您无法以如此简单的方式终止进程,这就是我测试 os.name 的原因.

On windows you cannot kill a process in such simple way that's why I test the os.name.

这是一种非常原始的方法,所以我宁愿建议找出问题的原因.

It's a pretty raw approach so I'd rather recommend to find the cause of the issue.

这篇关于为什么不能从 Kivy 终止这个 Python 多处理进程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆