1. ホーム
  2. python

[解決済み] Pythonでループするスレッドを停止するには?

2023-06-20 07:42:15

質問

ループしているスレッドにループを止めるように指示する適切な方法は何ですか?

私は、指定されたホストに ping を実行する非常に単純なプログラムを持っており、別の threading.Thread クラスで指定されたホストに ping します。このクラスでは、60 秒間スリープし、アプリケーションが終了するまで再び実行されます。

の中に'Stop'ボタンを実装したいと思います。 wx.Frame で、ループしているスレッドに停止を要求するための 'Stop' ボタンを実装したいと思います。すぐにスレッドを終了させる必要はなく、一度起動したらループを停止させるだけでよいのです。

以下は、私の 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
    

if __name__ == "__main__":
    main()

トリックは、実行中のスレッドが追加のプロパティを持つことができるということです。この解決策は を仮定しています。

  • スレッドはデフォルト値でプロパティ "do_run" を持っています。 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()

編集:Python3.6で試してみました。 stop_event.wait() はイベントをブロックし、whileループを解除します。これはブール値を返しません。使用方法 stop_event.is_set() を使用すると、代わりに動作します。

一つの錠剤で複数のスレッドを停止させる

複数のスレッドを一度に停止させる場合、1 つの錠剤がすべてのスレッドに効くため、殺すための錠剤の利点がよりよくわかります。 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()