为什么后台任务会阻止 SimpleHTTPServer 中的响应?

发布于 2024-09-28 15:49:35 字数 1468 浏览 2 评论 0原文

我正在编写一个简单的基于浏览器的前端,它应该能够启动后台任务,然后从中获取进度。我希望浏览器收到一个响应,说明任务是否成功启动,然后轮询以确定任务何时完成。但是,后台任务的存在似乎会阻止立即发送 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

那小子欠揍 2024-10-05 15:49:35

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

确定内容长度的方法有点难看。如果您不指定,尽管显示了内容,浏览器仍可能显示旋转的光标。关闭 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)

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.

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)
惜醉颜 2024-10-05 15:49:35

我使用此代码片段来运行 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()

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.

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()
小情绪 2024-10-05 15:49:35

答案是多处理模块使用自己的标准输出创建一个完全不同的进程...因此您的应用程序就像您编写的那样运行:

  1. 您启动应用程序
    你的终端窗口。
  2. 单击浏览器中的“运行”按钮
    它在 /run 上执行 GET
  3. 您可以在中看到当前进程的输出
    您的终端窗口,“正在开始..”
  4. 一个新进程启动,MyProc
    有自己的标准输出和标准错误。
  5. MyProc 打印到它的标准输出(其中
    无处可去),'开始很长
    过程..'。
  6. MyProc 启动的那一刻,您的应用程序就会打印到
    标准输出,“启动后。”因为它是
    没有被告知要等待任何类型的
    执行之前来自 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:

  1. You start up the application in
    your terminal window.
  2. You click on the Run button in your browser
    which does a GET on /run
  3. You see the output of the current process in
    your terminal window, "Starting.."
  4. A new process is started, MyProc
    with its own stdout and stderr.
  5. MyProc prints to its stdout (which
    goes nowhere), 'Starting long
    process..'.
  6. The very moment MyProc starts up, your app prints to
    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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文