ループするスレッドにループを停止するよう指示する適切な方法は何ですか?
別のthreading.Thread
クラスで指定されたホストにpingを実行するかなり単純なプログラムがあります。このクラスでは60秒スリープし、アプリケーションが終了するまで再び実行されます。
wx.Frame
に[停止]ボタンを実装して、ループするスレッドに停止するように依頼します。スレッドをすぐに終了する必要はありません。起動するとループを停止できます。
ここに私のthreading
クラスがあります(注:まだループを実装していませんが、PingAssetsのrunメソッドに該当する可能性があります)
class PingAssets(threading.Thread):
def __init__(self, threadNum, asset, window):
threading.Thread.__init__(self)
self.threadNum = threadNum
self.window = window
self.asset = asset
def run(self):
config = controller.getConfig()
fmt = config['timefmt']
start_time = datetime.now().strftime(fmt)
try:
if onlinecheck.check_status(self.asset):
status = "online"
else:
status = "offline"
except socket.gaierror:
status = "an invalid asset tag."
msg =("{}: {} is {}. \n".format(start_time, self.asset, status))
wx.CallAfter(self.window.Logger, msg)
そして、私のwxPyhtonフレームには、[スタート]ボタンから呼び出されるこの関数があります。
def CheckAsset(self, asset):
self.count += 1
thread = PingAssets(self.count, asset, self)
self.threads.append(thread)
thread.start()
threading.Thread
をサブクラス化する代わりに、関数を修正してフラグによる停止を許可できます。
実行中の関数にアクセス可能なオブジェクトが必要です。このオブジェクトにフラグを設定して実行を停止します。
threading.currentThread()
オブジェクトを使用できます。
import threading
import time
def doit(arg):
t = threading.currentThread()
while getattr(t, "do_run", True):
print ("working on %s" % arg)
time.sleep(1)
print("Stopping as you wish.")
def main():
t = threading.Thread(target=doit, args=("task",))
t.start()
time.sleep(5)
t.do_run = False
t.join()
if __== "__main__":
main()
秘trickは、実行中のスレッドに追加のプロパティを付加できることです。ソリューションは前提に基づいて構築されます。
True
のプロパティ「do_run」がありますFalse
に割り当てることができます。コードを実行すると、次の出力が得られます。
$ python stopthread.py
working on task
working on task
working on task
working on task
working on task
Stopping as you wish.
他の選択肢は、threading.Event
を関数の引数として使用することです。デフォルトではFalse
ですが、外部プロセスは(True
に) "設定"でき、関数はwait(timeout)
関数を使用して学習できます。
タイムアウトなしでwait
できますが、スリープタイマーとして使用することもできます(以下で使用)。
def doit(stop_event, arg):
while not stop_event.wait(1):
print ("working on %s" % arg)
print("Stopping as you wish.")
def main():
pill2kill = threading.Event()
t = threading.Thread(target=doit, args=(pill2kill, "task"))
t.start()
time.sleep(5)
pill2kill.set()
t.join()
編集:Python 3.6でこれを試しました。 stop_event.wait()
は、リリースされるまでイベント(およびwhileループ)をブロックします。ブール値を返しません。代わりにstop_event.is_set()
を使用すると機能します。
一度に複数のスレッドを停止する必要がある場合、1つのピルがすべてに有効であるため、ピルを殺すことの利点がよく見られます。
doit
はまったく変更されず、main
のみがスレッドを少し異なる方法で処理します。
def main():
pill2kill = threading.Event()
tasks = ["task ONE", "task TWO", "task THREE"]
def thread_gen(pill2kill, tasks):
for task in tasks:
t = threading.Thread(target=doit, args=(pill2kill, task))
yield t
threads = list(thread_gen(pill2kill, tasks))
for thread in threads:
thread.start()
time.sleep(5)
pill2kill.set()
for thread in threads:
thread.join()
これはStackで以前に尋ねられました。次のリンクを参照してください。
基本的には、スレッドがチェックするセンチネル値を設定する停止関数を使用してスレッドをセットアップするだけです。あなたの場合、ループ内の何かにセンチネル値をチェックして、変更されているかどうかを確認し、変更されている場合、ループが壊れてスレッドが死ぬ可能性があります。
Stackに関する他の質問を読みましたが、クラス間でのコミュニケーションについてはまだ少し混乱していました。ここに私がどのようにアプローチしたかがあります:
リストを使用して、wxFrameクラスの__init__
メソッドですべてのスレッドを保持します:self.threads = []
Pythonでループスレッドを停止するには? で推奨されているように、スレッドクラスを初期化するときに、True
に設定されたスレッドクラスでシグナルを使用します。
class PingAssets(threading.Thread):
def __init__(self, threadNum, asset, window):
threading.Thread.__init__(self)
self.threadNum = threadNum
self.window = window
self.asset = asset
self.signal = True
def run(self):
while self.signal:
do_stuff()
sleep()
スレッドを反復処理することで、これらのスレッドを停止できます。
def OnStop(self, e):
for t in self.threads:
t.signal = False
別のアプローチがありました。 Threadクラスをサブクラス化し、コンストラクターでEventオブジェクトを作成しました。次に、最初にこのイベントを設定してから親のバージョン自体を呼び出すカスタムjoin()メソッドを作成しました。
これが私のクラスです。wxPythonアプリのシリアルポート通信に使用しています。
import wx, threading, serial, Events, Queue
class PumpThread(threading.Thread):
def __init__ (self, port, queue, parent):
super(PumpThread, self).__init__()
self.port = port
self.queue = queue
self.parent = parent
self.serial = serial.Serial()
self.serial.port = self.port
self.serial.timeout = 0.5
self.serial.baudrate = 9600
self.serial.parity = 'N'
self.stopRequest = threading.Event()
def run (self):
try:
self.serial.open()
except Exception, ex:
print ("[ERROR]\tUnable to open port {}".format(self.port))
print ("[ERROR]\t{}\n\n{}".format(ex.message, ex.traceback))
self.stopRequest.set()
else:
print ("[INFO]\tListening port {}".format(self.port))
self.serial.write("FLOW?\r")
while not self.stopRequest.isSet():
msg = ''
if not self.queue.empty():
try:
command = self.queue.get()
self.serial.write(command)
except Queue.Empty:
continue
while self.serial.inWaiting():
char = self.serial.read(1)
if '\r' in char and len(msg) > 1:
char = ''
#~ print('[DATA]\t{}'.format(msg))
event = Events.PumpDataEvent(Events.SERIALRX, wx.ID_ANY, msg)
wx.PostEvent(self.parent, event)
msg = ''
break
msg += char
self.serial.close()
def join (self, timeout=None):
self.stopRequest.set()
super(PumpThread, self).join(timeout)
def SetPort (self, serial):
self.serial = serial
def Write (self, msg):
if self.serial.is_open:
self.queue.put(msg)
else:
print("[ERROR]\tPort {} is not open!".format(self.port))
def Stop(self):
if self.isAlive():
self.join()
キューはメッセージをポートに送信するために使用され、メインループは応答を返します。最終行の文字が異なるため、serial.readline()メソッドを使用しませんでした。また、ioクラスの使用法が非常に面倒であることがわかりました。