問題描述
我正在嘗試從 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:
信號處理程序正在攔截 Process.terminate() 發送的 TERM 請求并忽略它.要驗證這一點,只需在新進程中使用 signal.getsignal(signal.SIGTERM) 并打印結果.為了避免此類問題,您可以使用 signal.signal(signal.SIGTERM, signal.SIG_DFL) 重置默認行為,但請記住,SIGTERM 可能被框架靜音是有原因的(我對 Django 也不熟悉Kivy 也不行).
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 多處理進程?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!