2

我正在编写一个简单的基于浏览器的前端,它应该能够启动后台任务,然后从中获取进度。我希望浏览器收到一个响应,说明任务是否成功启动,然后轮询以确定它何时完成。但是,后台任务的存在似乎阻止了 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 试过这个,但遇到了类似的问题。有任何想法吗?

4

3 回答 3

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)
于 2010-10-20T15:45:55.783 回答
2

我使用这个片段来运行 SimpleHTTPServer 的线程版本。

我将这个文件保存ThreadedHTTPServer.py为例如,然后我像这样运行:

$ python -m /path/to/ThreadedHTTPServer PORT

所以它会在单独的线程中受到威胁,现在您可以并行下载并正确导航。

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()
于 2013-02-06T13:11:49.833 回答
0

答案是多处理模块使用自己的标准输出分叉了一个完全不同的进程......所以你的应用程序就像你写的那样运行:

  1. 您在终端窗口中启动应用程序。
  2. 您单击浏览器中的“运行”按钮,该按钮在 /run 上执行 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() 在继续之前等待响应。

于 2010-10-20T02:15:06.390 回答