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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

1202閲覧

pingをスレッドに分けて実行すると正常値が返ってこない

sky6720

総合スコア19

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2020/02/14 00:04

pythonのモジュールpingsを使用してネットワーク上にいるPCの死活管理をしようとしております。
tkinterを用いて全PCを一覧表示してpingが通れば名前の前に□を、通らなければ■を付けて表示する機能を持たせました。

上から順番にpingを飛ばした場合は正常な値が返ってくるのですが、
それだと処理に時間がかかるためPCごとにスレッドに分けてpingを飛ばすと全てpingが通る判定となってしまいます。

ソースは下記の通りですが、なぜこのような現象になるのかご教授ください。

また、終了時に[×]ボタンで終了すると例外エラーが表示されるので
ボタンを押して全スレッドが終了するのを待つような処理を実装しようとしましたが、
これもまたうまく動作しません。
こちらも合わせてご教授いただけると幸甚です。

python

1import tkinter as tk 2import pings 3import threading as th 4 5PEOPLE = { 6 '■ user1':'PC1', 7 '■ user2':'PC2', 8 '■ user3':'PC3', 9 '■ user4':'PC4' 10 } 11 12LIST_PEOPLE = list(PEOPLE.keys()) 13root = tk.Tk() 14 15def callback(): 16 global thre 17 thre = [] 18 for mem in range(len(LIST_PEOPLE)): 19 thre.append(th.Thread(target=pin, args=([mem, PEOPLE[LIST_PEOPLE[mem]]]))) 20 thre[mem].start() 21 22def pin(mem, pc): 23 p = pings.Ping() 24 res = p.ping(pc) 25 if res.is_reached(): 26 lbl[mem]['text'] = '□' + lbl[mem]['text'][1:] 27 else: 28 lbl[mem]['text'] = '■' + lbl[mem]['text'][1:] 29 root.after(5000, pin(mem, pc)) 30 31def close_win(): 32 for thread in thre: 33 thread.join() 34 35lbl = [] 36j = 0 37for i in LIST_PEOPLE: 38 lbl.append(tk.Label(root, text=i, font=('consolas', 10))) 39 lbl[j].pack(anchor=tk.NW, padx=20) 40 j += 1 41btn = tk.Button(root, text='close', font=('consolas', 10), command=close_win) 42btn.pack() 43callback() 44 45root.mainloop()

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自信ないですが、pings.Ping()で各スレッド毎にユニークなown_id(16bit整数値)をセットしてやる必要があると思います。

応答が返ってきたときに、自分が送ったpingに対する応答かをown_idで判定しているようです。
own_idのデフォルト値はプロセスIDから作っているので、すべてのスレッドのPingインスタンスで同じ値になってしまっています。
そのため、一つのホストからpingの応答があると、すべてのスレッドのPingインスタンスが、自分への応答が返ってきたと勘違いしてるのではないかと。

元のプログラムだとpings.Ping(own_id=mem)で、とりあえずは動くかな。

投稿2020/02/14 03:57

bsdfan

総合スコア4520

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sky6720

2020/02/14 04:09

ご回答ありがとうございます。 own_idで正常に動作することを確認できました。 ただ、スレッドの終了処理が未だ例外エラーとなります。
bsdfan

2020/02/14 07:04 編集

スレッドの終了については、今回のプログラムでは、root.afterのところが間違っていて、コールバックではなく、即時評価の再帰になっています。 そのため、スレッドが再帰のリミットに到達してエラーとなるまで、いつまで待っても終わらないです。 スレッドを終了させる仕組みを取り入れるなら、shiracamusさんの回答が良い例だと思います。
guest

0

ベストアンサー

root.after を使うとメインスレッドから呼び出されてしまいます。普通に time.sleep するといいです。
tkinterはシングルスレッドで動作することを前提に作られているようで、サブスレッドからは 変数ガジェット(*Var) にsetするのが無難です。
以下のようにすることで正常終了するようになりました。

python

1import time 2import pings 3import tkinter as tk 4import threading as th 5 6PEOPLE = { 7 'user1': 'PC1', 8 'user2': 'PC2', 9 'user3': 'PC3', 10 'user4': 'PC4' 11} 12 13root = tk.Tk() 14status = {user: tk.StringVar() for user in PEOPLE} 15 16def start_threads(): 17 ping.running = True 18 threads = [th.Thread(target=ping, args=(user,)) for user in PEOPLE] 19 for thread in threads: 20 thread.start() 21 return threads 22 23def stop_threads(threads): 24 ping.running = False 25 for thread in threads: 26 thread.join() 27 28def ping(user): 29 print("start ping:", user) 30 p = pings.Ping() 31 while ping.running: 32 res = p.ping(PEOPLE[user]) 33 print(user, res.messages) 34 if res.is_reached(): 35 status[user].set(f'□ {user}') 36 else: 37 status[user].set(f'■ {user}') 38 time.sleep(5) 39 print("stop ping:", user) 40 41def main(): 42 for user in PEOPLE: 43 label = tk.Label(root, textvariable=status[user], font=('consolas', 10)) 44 label.pack(anchor=tk.NW, padx=20) 45 46 btn = tk.Button(root, text='close', font=('consolas', 10), command=root.destroy) 47 btn.pack() 48 49 threads = start_threads() 50 print("started") 51 52 try: 53 root.mainloop() 54 except KeyboardInterrupt: 55 print() 56 print("killed") 57 else: 58 print("closed") 59 60 print("stopping...") 61 stop_threads(threads) 62 print("stopped") 63 64if __name__ == '__main__': 65 main()

投稿2020/02/14 01:23

編集2020/02/14 04:05
shiracamus

総合スコア5406

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sky6720

2020/02/14 04:06

ご回答ありがとうございます。 勉強不足で申し訳ありませんが、いただいた中の「ping.running」の箇所がわからないです。 このpingはdef ping:のものと同一でしょうか。 pingクラス(?)のなかのrunnningメソッドという意味でしょうか。 見当違いな質問であれば申し訳ありません。
shiracamus

2020/02/14 04:54 編集

ping関数オブジェクトに属性設定してます。 関数はFunctionクラスのインスタンスで、インスタンス変数を設定できるんですよ、不思議なことに。 普通は global変数にしますね。わかりにくくてすみません。
sky6720

2020/02/14 05:39

勉強不足で申し訳ありません。 なんとなくですがわかりました。また調べさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問