为什么后台任务会阻止 SimpleHTTPServer 中的响应?
我正在编写一个简单的基于浏览器的前端,它应该能够启动后台任务,然后从中获取进度。我希望浏览器收到一个响应,说明任务是否成功启动,然后轮询以确定任务何时完成。但是,后台任务的存在似乎会阻止立即发送 XMLHttpRequest 响应,因此我无法报告启动该进程的成功。考虑以下(简化的)代码:
import SocketServer
import SimpleHTTPServer
import multiprocessing
import time
class MyProc(multiprocessing.Process):
def run(self):
print 'Starting long process..'
for i in range(100): time.sleep(1)
print 'Done long process'
class Page(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
print >>self.wfile, "<html><body><a href='/run'>Run</a></body></html>"
if self.path == '/run':
self.proc = MyProc()
print 'Starting..'
self.proc.start()
print 'After start.'
print >>self.wfile, "Process started."
httpd = SocketServer.TCPServer(('', 8000), Page)
httpd.serve_forever()
当我运行此代码并浏览到 http://localhost:8000 时,我得到一个名为“运行”的按钮。当我单击它时,终端显示:
Starting..
After start.
但是浏览器视图不会改变..实际上光标在旋转。仅当我在终端中按 Ctrl-C 来中断程序时,浏览器才会更新并显示消息 Process started.
消息 After start
清楚地被打印出来。因此我可以假设 do_GET
在启动进程后返回。然而,直到我中断长时间运行的进程后,浏览器才会得到响应。我必须得出结论,do_GET
和正在发送的响应(位于 SimpleHTTPServer
内部)之间存在某些阻塞。
我也尝试过使用线程和 subprocess.Popen 但遇到了类似的问题。有什么想法吗?
I'm writing a simple browser-based front end that should be able to launch a background task and then get progress from it. I want the browser to receive a response saying whether the task launched successfully, and then poll to determine when it is done. However, the presence of a background task seems to be stopping the XMLHttpRequest response from being sent immediately, so I can't report the success of launching the process. Consider the following (simplified) code:
import SocketServer
import SimpleHTTPServer
import multiprocessing
import time
class MyProc(multiprocessing.Process):
def run(self):
print 'Starting long process..'
for i in range(100): time.sleep(1)
print 'Done long process'
class Page(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
print >>self.wfile, "<html><body><a href='/run'>Run</a></body></html>"
if self.path == '/run':
self.proc = MyProc()
print 'Starting..'
self.proc.start()
print 'After start.'
print >>self.wfile, "Process started."
httpd = SocketServer.TCPServer(('', 8000), Page)
httpd.serve_forever()
When I run this, and browse to http://localhost:8000, I get a button named "Run". When I click on it, the terminal displays:
Starting..
After start.
However the browser view does not change.. in fact the cursor is spinning. Only when I press Ctrl-C in the terminal to interrupt the program, then the browser is update with the message Process started.
The message After start
is clearly being printed. Therefore I can assume that do_GET
is returning after starting the process. Yet, the browser doesn't get a response until after I interrupt the long-running process. I have to conclude there is something blocking between do_GET
and the response being sent, which is inside SimpleHTTPServer
.
I've also tried this with threads and subprocess.Popen but ran into similar problems. Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
除了上面史蒂夫和我的评论之外,这里还有一个适合我的解决方案。
确定内容长度的方法有点难看。如果您不指定,尽管显示了内容,浏览器仍可能显示旋转的光标。关闭 self.wfile 也可以。
In addition to Steve's and my comments above, here is a solution that works for me.
The method to determine a content-length is a bit ugly. If you don't specify one, the browser may still show a spinning cursor although the content is shown. Closing the
self.wfile
instead could also work.我使用此代码片段来运行 SimpleHTTPServer 的线程版本。
例如,我将此文件保存为 ThreadedHTTPServer.py ,然后像这样运行:
$ python -m /path/to/ThreadedHTTPServer PORT
所以它将受到单独威胁线程,现在您可以并行下载并正确导航。
I use this snippet to run Threaded Version of SimpleHTTPServer.
I save this file as
ThreadedHTTPServer.py
for example and then I run like that:$ python -m /path/to/ThreadedHTTPServer PORT
So it'll be threated in separated threads and now you can download in paralell and navigate properly.
答案是多处理模块使用自己的标准输出创建一个完全不同的进程...因此您的应用程序就像您编写的那样运行:
你的终端窗口。
它在 /run 上执行 GET
您的终端窗口,“正在开始..”
有自己的标准输出和标准错误。
无处可去),'开始很长
过程..'。
标准输出,“启动后。”因为它是
没有被告知要等待任何类型的
执行之前来自 MyProc 的响应
所以。
您需要的是实现一个在主应用程序进程和分叉进程之间来回通信的队列。这里有一些关于如何执行此操作的特定于多处理的示例:
http://www .ibm.com/developerworks/aix/library/au-multiprocessing/
但是,那篇文章(像 IBM 的大多数文章一样)有点深奥且过于复杂...您可能想看一下更简单的文章如何使用“常规”队列模块的示例(它与多处理中包含的模块几乎相同):
http://www.artfulcode.net/articles/multi-threading-python/
要理解的最重要的概念是如何使用队列在进程之间打乱数据以及如何使用 join()等待响应后再继续。
The answer is that the multiprocessing module forks a completely different process with its own stdout... So your application is running just as you wrote it:
your terminal window.
which does a GET on /run
your terminal window, "Starting.."
with its own stdout and stderr.
goes nowhere), 'Starting long
process..'.
stdout, "After start." since it was
not told to wait for any kind of
response from MyProc before doing
so.
What you need is to implement a Queue that communicates back and forth between your main application's process and the forked process. There's some multiprocessing-specific examples on how to do that here:
http://www.ibm.com/developerworks/aix/library/au-multiprocessing/
However, that article (like most articles from IBM) is kind of deep and overly complicated... You might want to take a look at a simpler example of how to use the "regular" Queue module (it is pretty much identical to the one included in multiprocessing):
http://www.artfulcode.net/articles/multi-threading-python/
The most important concepts to understand are how to shuffle data between processes using the Queue and how to use join() to wait for a response before proceeding.