Python中线程局部变量的生命周期是什么?

14 投票
4 回答
4246 浏览
提问于 2025-04-15 14:36

在编程中,有时候我们需要把一些代码放在一起,这样可以让我们的程序更整洁,也更容易管理。比如说,如果你有一段代码需要重复使用,或者你想把一些相关的功能放在一起,这时候就可以用“函数”来帮忙。

函数就像一个小机器,你把输入放进去,它就会给你一个输出。你可以多次调用这个函数,而不需要每次都写一模一样的代码。这样不仅节省了时间,还减少了出错的机会。

另外,使用函数还可以让你的代码更容易理解。想象一下,如果你的程序里到处都是长长的代码,别人(或者未来的你)想要理解的时候就会很麻烦。但是如果你把相关的代码放进一个函数里,并给它起个简单明了的名字,别人就能很快明白这个函数的作用。

总之,函数是编程中一个非常重要的概念,它能帮助我们写出更简洁、更易懂的代码。

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 个回答

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!
11

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

我也开始思考这个问题,并尝试了一个测试程序,这个程序和其他答案中的类似。我的结论是,这些引用确实会在程序结束之前就被垃圾回收,也就是说,一旦线程结束,它们就可以被认为是垃圾了。

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

正如你所看到的,删除操作似乎是在线程结束后立即发生的。

3

马克的理解差不多是对的——简单来说,“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!

撰写回答