如何处理即将终止的线程?

时间:2013-01-08 12:32:49

标签: python multithreading

我正在运行一个即将终止的线程,但在那时,我已经想要摆脱它的引用。我可以开始并希望最好,还是应该以某种特殊的方式处理它?<​​/ p>

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

2 个答案:

答案 0 :(得分:4)

当留下while循环时,你应该在worker内引发的回调中将线程设置为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()告诉每个worker停止,每个worker调用一个回调,然后从所有worker的列表中删除该worker。

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

答案 1 :(得分:0)

作为一个已经提到过的评论者,你不能使用join(),因为所有这一切都是等待线程自然终止。虽然底层的线程库通常只有一个(例如pthread_kill()),但我不知道任何强制终止线程的本机Python API。这通常是因为在Python中强行杀死一个线程通常是一件非常糟糕的事情。

但是,在这种情况下,您似乎没有尝试杀死不合作的线程,您已向线程发出信号,要求它正常终止(通过设置running属性)。

我没有看到任何理由您不能立即将thread设置为None - 我不相信删除对线程对象的最后一个引用实际上会导致任何问题因为线程将一直保持活动状态,直到它终止(除非你在其上设置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