为什么不能从Kivy终止这个Python多处理过程?

时间:2014-11-16 16:40:54

标签: python django multiprocessing kivy

我试图在Kivy应用程序中运行django开发服务器。到目前为止,这确实很顺利。

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

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()

当我运行此代码时,一切都按预期工作。这是在控制台中发布的内容:

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类:

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()

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

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是否在某种程度上干扰了这个过程?

2 个答案:

答案 0 :(得分:3)

我认为这里的问题可能是两个:

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

  2. 如果你正在使用Python 2,你必须考虑解释器如果在线程库(Locks,Semaphores ...)或本机上的同步原语上被阻止,它就不会处理信号C电话。在这些情况下,serve_forever()函数可能会掉落(使用源的力!)。快速检查可能是尝试在Python 3上运行代码并查看它是否有效。

  3. 快速而肮脏的解决方案包括等待少量时间并在进程仍然存在时发送SIGKILL。

    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的原因。

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

答案 1 :(得分:1)

如果您拨打terminate(),然后join()并跳过while循环会怎样?此外,我稍微改变了代码并将一些代码考虑到_create_server()。如果这对您有用,请告诉我。

class DjangoServer():
    __metaclass__ = Singleton

    def _create_server(self):
        httpd = make_server('', 8000, django_wsgi_application())
        print "Now serving on port {}...".format(httpd.server_port)
        httpd.serve_forever()

    def start(self):
        self.server = multiprocessing.Process(target=self._create_server)
        self.server.start()
        print "Server Process PID = %s" %self.server.pid

    def stop(self):
        print("shutdown initiated")
        print "Server Process PID = %s" %self.server.pid
        self.server.terminate()
        self.server.join()
        print("server process terminated")