2

終了しようとしている実行中のスレッドがありますが、その時点で、すでにその参照を削除したいと考えています。私はそれを開始して最高のものを期待することができますか、それとも特別な方法で処理する必要がありますか?

class CoolThing(object):

    def __init__(self):
        self.thread = None

    def run_in_background(self, callback, period=0.5):
        if self.thread:
            raise RuntimeError

        def worker():
            worker.running = True
            while worker.running:
                if some_event():
                    callback(self)
                time.sleep(period)

        self.thread = (threading.Thread(target=worker), worker)
        self.thread[0].start()

    def stop_background(self, join=False):
        if not self.thread:
            raise RuntimeError

        # Make the worker function end harmfully.
        self.thread[1].running = False

        if join:
            self.thread[0].join()

        # What should I now do with the thread being about to
        # terminate, when not being joined?

        # ...

        self.thread = None
4

2 に答える 2

4

workerwhile ループが終了したときに、内から呼び出されるコールバックで thread を None に設定する必要があります。

編集:バックグラウンドプロセスの即時再起動もサポートするようになりました

import time
import threading

class CoolThing(object):

    def __init__(self):
        self.thread = None

    def run_in_background(self, callback, period=0.5):
        wait_count = 0
        while True:
            if self.thread:
                if self.thread[1].running or wait_count>10:
                    raise RuntimeError()
                time.sleep(0.5)
                wait_count += 1
            else:
                break

        def worker():
            t0 = time.time()
            worker.running = True
            while worker.running:
                if time.time()-t0>2:
                    callback()
                    t0 = time.time()
                time.sleep(period)
            worker.callback()

        worker.callback = self.dispose
        self.thread = (threading.Thread(target=worker), worker)
        self.thread[0].start()

    def stop_background(self, join=False):
        if not self.thread:
            raise RuntimeError
        self.thread[1].running = False
        if join:
            self.thread[0].join()
        self.stopping = True

    def dispose(self):
        self.thread = None
        self.stopping

def my_callback():
    print "Beep"

if __name__=="__main__":
    cool_thing = CoolThing()
    cool_thing.run_in_background(my_callback, 0.5)
    time.sleep(10)
    cool_thing.stop_background()
    # Immediatley restart process
    cool_thing.run_in_background(my_callback, 0.5)
    time.sleep(10)
    cool_thing.stop_background()
    print cool_thing.thread
    time.sleep(3)
    print cool_thing.thread

出力を与えます:

Beep
Beep
Beep
(<Thread(Thread-2, started 10760)>, <function worker at 0x02DEDD70>)
None

したがって、stop_background を呼び出した直後は、self.thread はまだ設定されていますが、後でNone. worker.callback-variable を保存して、その名前で dispose() を呼び出すこともできますが、この方法ではコードがより柔軟になります。

編集 2 : 新しい要件、新しいコード サンプル

ワーカー (SRP) 用に別のクラスを作成し、CoolThing はそのようなワーカーのリストを保持します。がrun_background(...)開始されると、まだ実行中のワーカーがあるかどうか (停止要求がないかどうか) を確認し、RuntimeError を発生させます。それ以外の場合は、新しいワーカーを開始します。 stop_background()各ワーカーに停止するように指示し、各ワーカーがコールバックを呼び出します。これにより、このワーカーがすべてのワーカーのリストから削除されます。

import time
import threading

class Worker(threading.Thread):
    def __init__(self, callback, period=0.5, finished_callback = None):
        threading.Thread.__init__(self)
        self.callback = callback
        self.period = period
        self._stop_requested = False
        self._finished_callback = finished_callback

    def run(self):
        t0 = time.time()
        while not self._stop_requested:
            if time.time()-t0>2:
                self.callback()
                t0 = time.time()
            time.sleep(self.period)
        if self._finished_callback:
            self._finished_callback(self)

    def request_stop(self):
        self._stop_requested = True

    @property
    def stopping(self):
        return self._stop_requested

class CoolThing(object):

    def __init__(self):
        self.workers = []
        self.workers_lock = threading.Lock()

    def run_in_background(self, callback, period=0.5):
        if len([w for w in self.workers if not w.stopping])>0:
            raise RuntimeError()
        worker = Worker(callback, period, finished_callback=self.dispose)
        with self.workers_lock:
            self.workers.append(worker)
        worker.start()

    def stop_background(self, join=False):
        if len(self.workers) == 0:
            raise RuntimeError()
        for worker in self.workers:
            worker.request_stop()
        if join:
            for worker in self.workers:
                worker.join()

    def dispose(self, worker):
        with self.workers_lock:
            self.workers.remove(worker)

def my_callback():
    print "Beep"

if __name__=="__main__":
    cool_thing = CoolThing()
    cool_thing.run_in_background(my_callback, 0.5)
    time.sleep(10) 
    print cool_thing.workers
    cool_thing.stop_background()
    # Immediatley restart process
    cool_thing.run_in_background(my_callback, 0.5)    
    print cool_thing.workers
    time.sleep(5)
    print cool_thing.workers
    time.sleep(5)
    cool_thing.stop_background()
    print cool_thing.workers
    time.sleep(3)
    print cool_thing.workers
于 2013-01-08T12:52:56.670 に答える
0

すでに述べたようjoin()に、スレッドが自然に終了するのを待つだけなので、使用することはできません。基になるスレッドライブラリには通常1つありますが(たとえば)、スレッドを強制的に強制終了するネイティブPythonAPIを認識していませんpthread_kill()。これは一般的に、Pythonでスレッドを強制的に強制終了することは一般的にかなり悪いことであるためです。

ただし、この場合、非協力的なスレッドを強制終了しようとしているようには見えません。(running属性を設定することにより)正常に終了するようにスレッドに通知しました。

threadすぐに設定できない理由はわかりませんNone-スレッドオブジェクトへの最後の参照を削除しても、スレッドが終了するまで存続するため、実際に問題が発生するとは思われません(に設定daemonするTrueと、メインプログラムが終了します)。もちろん、worker()関数を定義するときに作成されたクロージャー内にあるオブジェクトは引き続き存在するため、スレッドが終了するまで解放されません(おそらく、クロージャーの設定方法でも解放されません-私はする必要がありますそれについてもっと慎重に考えてください)。

とにかく、スレッドが終わったときにスレッドをアレンジするだけで、あなたの生活は楽になると思いますjoin()。スレッドの期間がタイムアウトするのを待たなければならないことが懸念される場合は、threading.Conditionオブジェクトを使用してそれを回避できます。

class CoolThing(object):

    def __init__(self):
        self.thread = None
        self.exit_condition = threading.Condition()

    def run_in_background(self, callback, period=0.5):
        if self.thread:
            raise RuntimeError

        def worker(exit_condition):
            exit_condition.acquire()
            worker.running = True
            while worker.running:
                if some_event():
                    callback(self)
                exit_condition.wait(period)
            exit_condition.release()

        self.thread = (threading.Thread(target=worker, args=(self.exit_condition,)),
                       worker)
        self.thread[0].start()

    def stop_background(self):
        if not self.thread:
            raise RuntimeError

        # Make the worker function end harmfully.
        self.exit_condition.acquire()
        self.thread[1].running = False
        self.exit_condition.notify()
        self.exit_condition.release()

        self.thread[0].join()
        self.thread = None
于 2013-01-08T13:17:02.370 に答える