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

worker当 while 循环离开时,您应该在从 within 调用的回调中将 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(),因为所做的只是等待线程自然终止。我不知道有任何本机 Python API 可以强制终止线程,尽管底层线程库通常会有一个(例如pthread_kill())。这通常是因为在 Python 中强行杀死一个线程通常是一件非常糟糕的事情。

然而,在这种情况下,您似乎并没有试图杀死一个不合作的线程,您已经向线程发出信号,希望它优雅地终止(通过设置您的running属性)。

我看不出有什么理由不能立即设置threadNone- 我不相信删除对线程对象的最后一个引用实际上会导致任何问题,因为线程将保持活动状态直到它终止(除非你'已经设置daemonTrue它并且主程序终止)。当然,在定义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 回答