Почему фоновая задача блокирует ответ в SimpleHTTPServer? - PullRequest
2 голосов
/ 20 октября 2010

Я пишу простой интерфейс на основе браузера, который должен иметь возможность запускать фоновую задачу, а затем получать от нее прогресс. Я хочу, чтобы браузер получил ответ о том, успешно ли запущена задача, а затем опросить, чтобы определить, когда она будет выполнена. Однако наличие фоновой задачи, по-видимому, препятствует немедленной отправке ответа 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.

Я также пробовал это с потоками и подпроцессом. Откройте, но столкнулся с похожими проблемами. Есть идеи?

Ответы [ 3 ]

3 голосов
/ 20 октября 2010

В дополнение к Стиву и моим комментариям выше, здесь есть решение, которое работает для меня.

Метод определения длины содержимого немного уродлив. Если вы не укажете один из них, браузер может по-прежнему отображать вращающийся курсор, хотя содержимое отображается. Вместо этого может также работать закрытие 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)
2 голосов
/ 06 февраля 2013

Я использую этот фрагмент для запуска многопоточной версии 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()
0 голосов
/ 20 октября 2010

Ответ заключается в том, что многопроцессорный модуль разветвляет совершенно другой процесс со своим собственным стандартным выводом ... Итак, ваше приложение работает так, как вы его написали:

  1. Вы запускаете приложение в окно вашего терминала.
  2. Вы нажимаете на кнопку Run в вашем браузере который делает GET на / запустить
  3. Вы видите результат текущего процесса в окно вашего терминала "Starting .."
  4. Новый процесс запущен, MyProc со своим собственным stdout и stderr.
  5. MyProc печатает на свой стандартный вывод (который не идет никуда) процесс .. ".
  6. В тот самый момент, когда MyProc запускается, ваше приложение печатает на стандартный вывод "После запуска". так как это было не сказал ждать какого-либо ответ от MyProc, прежде чем делать поэтому.

Что вам нужно, так это реализовать очередь, которая обменивается данными между процессом вашего основного приложения и разветвленным процессом. Вот несколько примеров, относящихся к многопроцессорности, о том, как это сделать:

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

Тем не менее, эта статья (как и большинство статей из IBM) является довольно глубокой и чрезмерно сложной ... Возможно, вы захотите взглянуть на более простой пример того, как использовать «обычный» модуль Queue (это в значительной степени идентичный тому, что входит в многопроцессорность):

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

Наиболее важные понятия, которые необходимо понять, - это как перетасовывать данные между процессами, использующими Очередь, и как использовать join () для ожидания ответа перед продолжением.

...