質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.90%

マルチスレッドで発生したプロセスを kill したい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 169

kirakuni

score 6

前提・実現したいこと

以前、マルチスレッドの mainloop() の抜け方について質問した者です。

プログラム自体は思い通りに動いているのですが、最近になってプログラムを実行するとプロセスがどんどん蓄積していくことに気づきました。
蓄積してもプログラム自体は動くのでそれでもいいような気もするのですが、やはり本来は新たに生じたプロセスをその都度 kill した方がいいように思っています。

発生している問題・エラーメッセージ

エラーが発生しているわけではないのですが、プロセスがどんどん蓄積することが問題なのではないかと感じています。

該当のソースコード

import shutil
import subprocess
import sys
import paramiko
import scp
import os
import cgi
import concurrent.futures
from tkinter import *
from tkinter import ttk

root = Tk()

def delDir():
    dir = '/mnt/HDD/localPyserver/dcmTemp'
    for root, dirs, files in os.walk(dir, topdown=False):
        for name in files:
            os.remove(os.path.join(root, name))
        for name in dirs:
            os.rmdir(os.path.join(root, name))    

def scp_get( remoteDir ): 
    with paramiko.SSHClient() as ssh:
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(hostname='10.242.134.115', port=22, username='user', password='pass')            
        with scp.SCPClient(ssh.get_transport()) as scpc:          
            try: 
                remotePath = remoteDir
                print(remotePath)
                scpc.get( remote_path = remoteDir, local_path = '/mnt/HDD/localPyserver/dcmTemp/', recursive = True)
        except:
                print('そのディレクトリは存在しません。')

def weasis()    :
    try:
        subprocess.run('javaws /mnt/HDD/localPyserver/weasis/weasisStart.jnlp', shell=True, check=True)
    except subprocess.CalledProcessError:
        print('外部プログラムの実行に失敗しました', file=sys.stderr) 

def func2():           
    pbIndeterminateVer = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='indeterminate')
    pbIndeterminateVer.pack(side="left")           
    pbIndeterminateVer.start(10)
    root.geometry("200x40")
    root.title("download")
    root.mainloop()

if __name__ == '__main__':

    executor = concurrent.futures.ThreadPoolExecutor(max_workers=2)
    form = cgi.FieldStorage()
    path = form.getvalue('path','')
    delDir()
    executor.submit( scp_get, path )
    func2()
    weasis()

試したこと

いろいろ実験してみて、func2()を実行すると新たなプロセスが以下のように蓄積していきます。

ps -fA | grep python

root      977     1  0 21:02 ?        00:00:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers
user     1645  1631  0 21:02 ?        00:00:00 /home/user/anaconda3/bin/python cgiserver.py
user     1905  1872  0 21:02 ?        00:00:00 python3 /usr/lib/blueberry/safechild /usr/sbin/rfkill event
user     2049  1645  1 21:02 ?        00:00:01 /home/user/anaconda3/bin/python3.7 /mnt/HDD/localPyserver/cgi-bin/openweasis.py
user     2217  1645  0 21:03 ?        00:00:00 /home/user/anaconda3/bin/python3.7 /mnt/HDD/localPyserver/cgi-bin/openweasis.py
user     2362  1645 10 21:04 ?        00:00:02 /home/user/anaconda3/bin/python3.7 /mnt/HDD/localPyserver/cgi-bin/openweasis.py
user     2481  2459  0 21:04 pts/0    00:00:00 grep --color=auto python

そこで、root.destroy() や root.kill() や root.quit() を最後に追加してみても効果はありませんでした。

また、「http://weekendproject9.hatenablog.com/entry/2019/03/01/124444」に書いてあるようなことをやってみると、
意図しないプロセスまで kill されてしまいます。

func2() で発生したプロセスだけを kill することはできるでしょうか?

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • otn

    2020/07/14 09:31

    タイトルがおかしい。スレッドとプロセスは違います。マルチプロセスですね。

    キャンセル

回答 1

checkベストアンサー

0

Windows10環境でテストしてみましたがroot.mainloop()から先に進みません(weasis()まで到達していない)
Linuxだと到達しているのかもしれませんが、想定通り動作していますか?

それはともかく、プロセスが残っているのは以下の2つが原因だと推測されます。

mainloop()を止める手段を講じていない

時間のかかる処理(scp_get)が終了した時点でroot.destroy()すればプログレスバーのウィンドウは破棄されます。

def scp_get(remoteDir):
    (略)
    try:
        (略)
    except:
        (略)
    finally:  # 処理が成功でも失敗でも確実にdestroyできるようfinallyに追加
        root.destroy()

追記

cgi経由だとroot.destroy()ではプロセスが終了しないことを確認しました。root.quit()ならプロセスが終了します。(windowsでの確認ですが)

subprocessで渡しているプログラムが終了するまでこのプログラムも終了しない。

subprocess.runは相手方のプログラムの終了を待ち合わせます。今回の場合特に相手の終了を待つ必要はないと思われますので相手のプログラムを起動したらpython側はすぐ終了できるようPopenで起動しましょう。Popenでは特別な設定を講じなければ相手プログラムの終了の待ち合わせを行いません。

- subprocess.run('javaws /mnt/HDD/localPyserver/weasis/weasisStart.jnlp', shell=True, check=True)
+ subprocess.Popen('javaws /mnt/HDD/localPyserver/weasis/weasisStart.jnlp', shell=True)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/14 12:39

    hope_mucciさん、ありがとうございます。

    環境に関して全く書いておりませんでした。

    ubuntu server 18.04 上に tornado があって、データベースで抽出した path をローカルの Linux mint が受け取って、その path を元に dicom ファイルを NAS から scp ダウンロードして、その後 weasis を起動して dicom ファイルを閲覧しています。
    そのためには、ubuntu server の tornado もサーバー起動し、またローカルの linux mint でも cgiserver を起動しておく必要がありますが、一応イメージ通りに閲覧できています。

    今、自宅ではないので、プログラムを少し変更してLinux mint 同士で実験してみました。
    その結果はやはり dicom ファイルは閲覧できていますが、挙動が少しだけ異なります。

    subprocess.run('javaws /mnt/HDD/localPyserver/weasis/weasisStart.jnlp', shell=True, check=True)で起動したjavaウインドウを閉じても、プログラムが完全には停止しません。

    subprocess.Popen('javaws /home/mituo/localPyserver/weasis/weasisStart.jnlp', shell=True)
    finally:
    root.destroy()

    に変更しても結果は同じです。
    そして、新たなプロセスが蓄積していきます。

    ところが以下のようにすると、新しいプロセスの蓄積はなくなります。

    if __name__ == '__main__':
    path = '/home/fuji/DCM'
    delDir()
    scp_get( path )
    weasis()

    自宅での実験でも、func2()を呼び出さないようにすればプロセスの蓄積はありませんでした。
    なので func2() の私のコードに問題があるような気がしているのですが、どうでしょうか?

    キャンセル

  • 2020/07/14 14:44

    検索したらwebサイトを発見したのでそちらも参考にしています。

    cgiサーバ経由で試してみたところ、確かにコメント通りの挙動になりました。
    追記した通り、destroyの代わりにquitを使うとプロセスが終了するようになりました。試してみてください。
    あと、root.quit()はscp_get関数の中で行ってください。分岐した側のスレッドで実行しないとrootを殺せません。

    キャンセル

  • 2020/07/15 12:31

    hope_mucciさん御回答ありがとうございます。

    linux mint 間ではありますが、おっしゃるようにしたら新しいプロセスが蓄積されなくなりました。

    あなたのおかげで、いとも簡単に問題が解決しました。
    ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.90%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る