2010-10-19 31 views
2

我正在写一个简单的基于浏览器的前端,应该能够启动后台任务,然后从中获得进展。我希望浏览器收到一个响应,说明任务是否成功启动,然后轮询以确定何时完成。但是,后台任务似乎阻止了立即发送XMLHttpRequest响应,所以我无法报告启动该进程的成功。考虑以下(简化)代码:为什么后台任务会阻止SimpleHTTPServer中的响应?

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,但遇到类似的问题。有任何想法吗?

+0

p.s.我正在使用Python 2.6.5。没有在其他版本上测试过。 – Steve 2010-10-20 01:58:11

+0

您不发送HTTP响应。请先查看'do_GET'的基本实现在覆盖之前实际执行的操作,而不提供适当的替代。 – 2010-10-20 10:53:38

+0

在“进程启动”之前添加'print >> self.wfile,“Content-Type:text/plain \ n”'没有实际意义,结果是一样的。 – Steve 2010-10-20 13:16:26

回答

3

除了史蒂夫和我上面的评论,这里有一个适合我的解决方案。

确定内容长度的方法有点难看。如果您没有指定,则虽然显示内容,但浏览器仍可能显示旋转光标。关闭self.wfile也可以工作。

from cStringIO import StringIO 

class Page(SimpleHTTPServer.SimpleHTTPRequestHandler): 
    def do_GET(self): 
     out = StringIO() 
     self.send_response(200) 
     self.send_header("Content-type", "text/html") 
     if self.path == '/': 
      out.write("<html><body><a href='/run'>Run</a></body></html>\n") 
     elif self.path == '/run': 
      self.proc = MyProc() 
      print 'Starting..' 
      self.proc.start() 
      print 'After start.' 
      out.write("<html><body><h1>Process started</h1></body></html>\n") 
     text = out.getvalue() 
     self.send_header("Content-Length", str(len(text))) 
     self.end_headers() 
     self.wfile.write(text) 
+0

酷,它的作品,谢谢你!当我说我在上面调用了'send_header'时,我只用它作为Content-type。看起来Content-length在这里很重要,我假设它意味着它已经传输了数据但并未实际关闭GET连接。我希望我能更深入地理解为什么......以某种方式打开后台进程可以阻止SimpleHTTPServer关闭连接?我在我的原始示例中尝试了'self.wfile.close()',但它没有改变任何东西。 – Steve 2010-10-20 17:42:54

+0

@Steve:HTTP连接通常保持打开状态,因为可以在不重新连接的情况下生成多个请求(管道传输)。还有一个HTTP标头('Connection:','keep-alive')。内容长度是通知浏览器何时收到所有数据所必需的。在我的示例中,Firefox可以呈现没有内容长度的页面,但显示旋转的光标。所有这些与你的后台进程无关。 – 2010-10-20 17:58:47

+0

我明白了。只是没有后台进程就不会发生同样的事情。无论如何,我明白,包含内容长度可能是一个好习惯。 – Steve 2010-10-21 03:37:03

0

的答案是,多模块叉拥有自己的标准输出一个完全不同的过程......所以,你的应用程序正在运行,就像你写的:

  1. 您在 终端窗口中启动应用程序。
  2. 您在浏览器中 这确实对GET /运行
  3. 你看 终端窗口当前进程的输出,点击运行按钮“开始。”
  4. 一个新进程启动,MyProc 有自己的stdout和stderr。
  5. MyProc打印到它的标准输出(其中 无处),'开始长 过程..'。
  6. MyProc启动时,您的应用打印到 标准输出“开始后”。因为它是 没有告诉等之前做的 响应从MyProc所以。

你需要的是实现一个队列,在主应用程序的进程和分叉进程之间来回通信。有一个关于如何做到这一点这里的一些具体多事例:

http://www.ibm.com/developerworks/aix/library/au-multiprocessing/

然而,该文章(如IBM的大多数文章)是一种深刻而过于复杂...你可能想看看如何使用“常规”队列模块简单的例子(这是几乎等同于多包括一个):

http://www.artfulcode.net/articles/multi-threading-python/

最重要的概念理解是如何在使用队列的进程之间混洗数据以及如何在继续之前使用join()等待响应。

+0

我的问题不是与多进程通信,我的问题是在浏览器中没有收到“进程已启动”消息,直到我查询多进程。我应该立即收到这条消息,因为“启动后”显然是打印出来的,但是直到另一个进程结束时才会有HTTP响应,尽管我并没有等待它。 – Steve 2010-10-20 13:12:44

1

我用这个片段来运行SimpleHTTPServer的Threaded版本。

我这个文件保存为ThreadedHTTPServer.py例如,然后我跑这样的:

$ python -m /path/to/ThreadedHTTPServer PORT

所以它会在分离线程threated,现在你可以使用并联下载和浏览正常。

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler 
from SocketServer import ThreadingMixIn 
import threading 
import SimpleHTTPServer 
import sys 

PORT = int(sys.argv[1]) 

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler 

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): 
    """Handle requests in a separate thread.""" 

if __name__ == '__main__': 
    server = ThreadedHTTPServer(('0.0.0.0', PORT), Handler) 
    print 'Starting server, use <Ctrl-C> to stop' 
    server.serve_forever() 
+0

优秀!这应该是被接受的答案。 – ccpizza 2017-09-03 08:03:54

相关问题