Python线程。线程,范围和垃圾收集

时间:2011-12-02 16:31:29

标签: python multithreading garbage-collection destructor

说我来自threading.Thread:

from threading import Thread

class Worker(Thread):
    def start(self):
        self.running = True
        Thread.start(self)

    def terminate(self):
        self.running = False
        self.join()

    def run(self):
        import time
        while self.running:
            print "running"
            time.sleep(1)

该类的任何实例都在启动线程之前必须让它的线程主动终止才能收集垃圾(线程本身拥有引用)。所以这是一个问题,因为它完全违背了垃圾收集的目的。在这种情况下,有一些对象封装了一个线程,并且对象的最后一个实例超出了范围,析构函数被调用以进行线程终止和清理。 Thuss a destructor

    def __del__(self):
        self.terminate()

不会这样做。

我看到很好地封装线程的唯一方法是使用低级thread内置模块和weakref弱引用。或者我可能会遗漏一些基本的东西。那么有没有比在weakref意大利面条代码中纠结的更好的方法呢?

3 个答案:

答案 0 :(得分:3)

如何使用包装器类(具有Thread而不是is-a Thread)?

例如:

class WorkerWrapper:
    __init__(self):
        self.worker = Worker()
    __del__(self):
        self.worker.terminate()

然后在客户端代码中使用这些包装类,而不是直接使用线程。

或许我想念一些事情(:

答案 1 :(得分:2)

要添加一个受@ datenwolf评论启发的答案,这是另一种处理被删除对象或父线程结束的方法:

import threading
import time
import weakref

class Foo(object):

    def __init__(self):
        self.main_thread = threading.current_thread()

        self.initialised = threading.Event()
        self.t = threading.Thread(target=Foo.threaded_func,
                args=(weakref.proxy(self), ))

        self.t.start()
        while not self.initialised.is_set():
            # This loop is necessary to stop the main threading doing anything
            # until the exception handler in threaded_func can deal with the 
            # object being deleted.
            pass

    def __del__(self):
        print 'self:', self, self.main_thread.is_alive()
        self.t.join()

    def threaded_func(self):

        self.initialised.set()

        try:
            while True:
                print time.time()

                if not self.main_thread.is_alive():
                    print('Main thread ended')
                    break

                time.sleep(1)

        except ReferenceError:
            print('Foo object deleted')

foo = Foo()
del foo
foo = Foo()

答案 2 :(得分:1)

我猜你是来自C ++的转换,其中很多含义可以附加到变量的范围,等于变量的生命周期。对于Python和垃圾收集语言来说,情况并非如此。 Scope!= Lifetime 只是因为只要解释器​​绕过它就会发生垃圾收集,而不是在范围边界上。特别是当你试图用它做异步的东西时,脖子上的凸起的毛发应该振动到你头脑中所有警告铃声的喧嚣! 您可以使用' del '来处理对象的生命周期。 (事实上​​,如果你阅读了cpython垃圾收集器模块的源代码,对那些在那里表达的终结器( del 方法)的对象的明显(并且有些有趣)鄙视,应该告诉每个人甚至使用它的生命周期只在必要时才提供对象)。

您可以使用sys.getrefcount(self)找出何时在您的线程中保留循环。但是我几乎不能推荐(只是尝试它返回的数字。你不会感到高兴。看看谁持有什么只是检查gc.get_referrers(self))。 引用计数可能/也将取决于垃圾收集。

此外,将执行线程的运行时绑定到对象的范围/生命周期是99%的错误。甚至Boost都没有。它超出了RAII的方式来定义称为“分离”的东西。线。 http://www.boost.org/doc/libs/1_55_0/doc/html/thread/thread_management.html