8

を使用してサブプロセスを開始するPythonプログラム(正確にはDjangoアプリケーション)がありますsubprocess.Popen。アプリケーションのアーキテクチャ上の制約によりPopen.terminate()、サブプロセスを終了しPopen.poll()たり、プロセスがいつ終了したかを確認したりするために使用することはできません。これは、開始されたサブプロセスへの参照を変数に保持できないためです。

代わりに、サブプロセスの開始時にプロセスIDpidをファイルに書き込む必要がありますpidfile。サブプロセスを停止したい場合は、これを開いてpidfile使用os.kill(pid, signal.SIGTERM)して停止します。

私の質問は、サブプロセスが実際に終了したことをどのように確認できますか?signal.SIGTERMを呼び出してから最終的に終了するには、使用に約1〜2分かかりos.kill()ます。最初はそれos.waitpid()がこのタスクにとって正しいことだと思いましたが、os.kill()それが私に与えられた後にそれを呼び出すとOSError: [Errno 10] No child processes

ちなみに、私は2つのフォームを使用してHTMLテンプレートからサブプロセスを開始および停止しており、プログラムロジックはDjangoビュー内にあります。アプリケーションがデバッグモードの場合、例外がブラウザに表示されます。python manage.py crawlwebpagesビュー( )で呼び出すサブプロセス自体が別のサブプロセス、つまりScrapyクローラーのインスタンスを呼び出すことを知っておくこともおそらく重要です。pidこのScrapyインスタンスのをに書き込みpidfileます。これが終了したいものです。

関連するコードは次のとおりです。

def process_main_page_forms(request):
    if request.method == 'POST':
        if request.POST['form-type'] == u'webpage-crawler-form':
            template_context = _crawl_webpage(request)

        elif request.POST['form-type'] == u'stop-crawler-form':
            template_context = _stop_crawler(request)
    else:
        template_context = {
            'webpage_crawler_form': WebPageCrawlerForm(),
            'stop_crawler_form': StopCrawlerForm()}

    return render(request, 'main.html', template_context)

def _crawl_webpage(request):
    webpage_crawler_form = WebPageCrawlerForm(request.POST)

    if webpage_crawler_form.is_valid():
        url_to_crawl = webpage_crawler_form.cleaned_data['url_to_crawl']
        maximum_pages_to_crawl = webpage_crawler_form.cleaned_data['maximum_pages_to_crawl']

        program = 'python manage.py crawlwebpages' + ' -n ' + str(maximum_pages_to_crawl) + ' ' + url_to_crawl
        p = subprocess.Popen(program.split())

    template_context = {
        'webpage_crawler_form': webpage_crawler_form,
        'stop_crawler_form': StopCrawlerForm()}

    return template_context

def _stop_crawler(request):
    stop_crawler_form = StopCrawlerForm(request.POST)

    if stop_crawler_form.is_valid():
        with open('scrapy_crawler_process.pid', 'rb') as pidfile:
            process_id = int(pidfile.read().strip())
            print 'PROCESS ID:', process_id

        os.kill(process_id, signal.SIGTERM)
        os.waitpid(process_id, os.WNOHANG) # This gives me the OSError
        print 'Crawler process terminated!'

    template_context = {
        'webpage_crawler_form': WebPageCrawlerForm(),
        'stop_crawler_form': stop_crawler_form}

    return template_context

私に何ができる?どうもありがとうございます!

編集:

Jacek Koniecznyすばらしい回答によると、関数のコードを次のように変更することで、問題を解決できました。_stop_crawler(request)

def _stop_crawler(request):
    stop_crawler_form = StopCrawlerForm(request.POST)

    if stop_crawler_form.is_valid():
        with open('scrapy_crawler_process.pid', 'rb') as pidfile:
            process_id = int(pidfile.read().strip())

        # These are the essential lines
        os.kill(process_id, signal.SIGTERM)
        while True:
            try:
                time.sleep(10)
                os.kill(process_id, 0)
            except OSError:
                break
        print 'Crawler process terminated!'

    template_context = {
        'webpage_crawler_form': WebPageCrawlerForm(),
        'stop_crawler_form': stop_crawler_form}

    return template_context
4

2 に答える 2

8

プロセスがまだ実行されているかどうかを確認する通常の方法は、シグナル「0」でプロセスをkill()することです。実行中のジョブには何も行わず、プロセスが存在しない場合はOSError例外が発生します。errno=ESRCH

[jajcus@lolek ~]$ sleep 1000 &
[1] 2405
[jajcus@lolek ~]$ python
Python 2.7.3 (default, May 11 2012, 11:57:22) 
[GCC 4.6.3 20120315 (release)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.kill(2405, 0)
>>> os.kill(2405, 15)
>>> os.kill(2405, 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 3] No such process

ただし、可能な場合は常に、呼び出し元は呼び出されたプロセスの親であり、wait()関数ファミリーを使用してその終了を処理する必要があります。それがPopenオブジェクトが行うことです。

于 2012-11-15T17:09:02.987 に答える
2

私の解決策は、サブプロセッシングを制御する中間プロセスを配置することです。

したがって、Webリクエスト(並列化のために、すべて異なるプロセスで発生するように見えます)は、制御プロセスに特定のプログラムを起動して監視するように指示します。必要に応じて、彼らはステータスが何であるかを尋ねます。

このプロセスは、最も単純なケースでは、UNIXドメインソケットを開き(TCP / IPソケットも同様に機能します)、それをリッスンするプロセスになります。「Webプロセス」はそれに接続し、起動要求を送信して、一意のIDを返します。その後、このIDを使用して、新しいプロセスでさらにクエリを実行できます。

または、IDを単独で提供する(または、プロセスが1つしかない場合は、IDをまったく使用しない)ため、変数IDを保持する必要はありません。

于 2012-11-15T14:45:06.263 に答える