Python中线程本地值的生命周期是多久?

13
import threading

mydata = threading.local()

def run():
    # When will the garbage collector be able to destroy the object created
    # here? After the thread exits from ``run()``? After ``join()`` is called?
    # Or will it survive the thread in which it was created, and live until
    # ``mydata`` is garbage-collected?
    mydata.foo = object()

t = threading.Thread(target=run)
t.start()
t.join()
4个回答

10

这是我的答案,因为我没有在之前的回答中看到结论。

我开始思考同样的问题,并尝试了一个类似于其他答案中的测试程序。我的结论是,它们比程序结束时更早地被垃圾收集,这意味着一旦线程本身死亡,这些引用就可以被确定为垃圾。

import time
import threading
import gc

data = threading.local()

class Resource(object):
    def __init__(self):
        self.name = threading.currentThread().name
        print 'create: %s' % self.name

    def __del__(self):
        print 'delete: %s' % self.name

def access_thlocal():
    data.key = Resource()

for i in range(0, 10):
    threading.Thread(target=access_thlocal).start()
time.sleep(1)
print "Triggering GC"
gc.collect()
time.sleep(1)

输出结果:

create: Thread-1
create: Thread-2
delete: Thread-1
create: Thread-3
delete: Thread-2
create: Thread-4
delete: Thread-3
create: Thread-5
delete: Thread-4
create: Thread-6
delete: Thread-5
create: Thread-7
delete: Thread-6
create: Thread-8
delete: Thread-7
create: Thread-9
delete: Thread-8
create: Thread-10
delete: Thread-9
Triggering GC
delete: Thread-10

你可以看到,删除似乎是在线程终止时立即发生的。


1
这似乎仍然是CPython 3.6中的当前行为。 - ash

3

Mark几乎说对了--实际上,“mydata”将持有其中所有TL变量的引用,无论它们是从哪个线程创建的。例如:

import threading
import gc

mydata = threading.local()

class x:
    def __del__(self):
        print "x got deleted!"

def run():
    mydata.foo = x()

t = threading.Thread(target=run)
print "t created"
gc.collect()
t.start()
print "t started"
gc.collect()
del mydata
print "mydata deleted"
gc.collect()
t.join()
print "t joined"
gc.collect()
print "Done!"

发出:

t created
t started
x got deleted!
mydata deleted
t joined
Done!

在CPython中,gc实际上没有任何作用,因此您可以将代码简化为:

import threading

mydata = threading.local()

class x:
    def __init__(self):
        print "x got created!"
    def __del__(self):
        print "x got deleted!"

def run():
    mydata.foo = x()

t = threading.Thread(target=run)
print "t created"
t.start()
print "t started"
del mydata
print "mydata deleted"
t.join()
print "t joined"
print "Done!"

并且仍然可以看到...

t created
x got created!
t started
x got deleted!
mydata deleted
t joined
Done!

1

谢谢!看起来Mark的程序在CPython 2.5和2.6下表现不同:

import threading
import gc
import platform

print "Python %s (%s)" % (platform.python_version(), " ".join(platform.python_build()))

mydata = threading.local()

class x:
    def __del__(self):
        print "x got deleted!"

def run():
    mydata.foo = x()

t = threading.Thread(target=run)
print "t created"
gc.collect()
t.start()
print "t started"
gc.collect()
del mydata
print "mydata deleted"
gc.collect()
t.join()
print "t joined"
gc.collect()
print "Done!"

在 Ubuntu 8.04 i386 下发出:

Python 2.5.2 (r252:60911 Jul 31 2008 19:40:22)
t created
t started
mydata deleted
x got deleted!
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.5/threading.py", line 486, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.5/threading.py", line 446, in run
    self.__target(*self.__args, **self.__kwargs)
  File "./x.py", line 14, in run
    mydata.foo = x()
NameError: global name 'mydata' is not defined

t joined
Done!

并且:

Python 2.6.2 (r262:71600 Sep 19 2009 17:24:20)
t created
t started
x got deleted!
mydata deleted
t joined
Done!

0
通过对程序进行一些简单的更改,并在每个线程步骤之后强制进行垃圾回收,似乎无法在程序完成之前收集foo - 换句话说,在线程超出范围之后。
import threading
import gc

mydata = threading.local()

class x:
    def __del__(self):
        print "x got deleted!"

def run():
    mydata.foo = x()

t = threading.Thread(target=run)
print "t created"
gc.collect()
t.start()
print "t started"
gc.collect()
t.join()
print "t joined"
gc.collect()
print "Done!"

输出(使用Python 2.6,Windows):

>C:\temp\py\t.py
t已创建
t已启动
t已加入
完成!
x已被删除!

好的分析,但是遗漏的部分是 mydata 的消失是你所需要的 -- 我需要打开一个新的答案来展示格式化的代码,但是要点与你的差不多。 - Alex Martelli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接