Python threading.Thread, области и сборка мусора - PullRequest
4 голосов
/ 02 декабря 2011

Скажем, я наследую от 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)

У любого экземпляра этого класса с запущенным потоком должен быть активно завершен поток, прежде чем он сможет получить сборщик мусора (поток сам содержит ссылку),Так что это проблема, потому что она не поддается цели сбора мусора.В этом случае, если какой-то объект инкапсулирует поток, а последний экземпляр объекта выходит из области действия , деструктор вызывается для завершения и очистки потока.Таким образом, деструктор

    def __del__(self):
        self.terminate()

не сработает.

Единственный способ красиво инкапсулировать потоки - использовать встроенный модуль низкого уровня thread и слабые ссылки weakref.Или я могу упустить что-то фундаментальное.Так есть ли лучший способ, чем запутывать вещи в weakref коде спагетти?

Ответы [ 3 ]

3 голосов
/ 02 декабря 2011

Как насчет использования класса-оболочки (который имеет -a Thread вместо is-a Thread)?

например:

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

И затем использовать эти классы-обертки в клиентском коде, а не напрямую в потоках.

Или, может быть, я что-то упустил (:

2 голосов
/ 12 февраля 2013

Чтобы добавить ответ, вдохновленный комментарием @ 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()
1 голос
/ 19 июня 2014

Полагаю, вы - конверт из 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

...