Python 的 object 没有如期通过__del__释放资源怎么办? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
nowheremanx
V2EX    Python

Python 的 object 没有如期通过__del__释放资源怎么办?

  •  
  •   nowheremanx 2023-10-06 12:01:38 +08:00 2641 次点击
    这是一个创建于 784 天前的主题,其中的信息可能已经有所发展或是发生改变。
    比如有一个 class Task ,创建的时候会生产不少本地临时文件,整个生命周期都会用这个文件。然而 Task 没有用之后,或者程序崩溃的时候,就应该合理地销毁这些临时文件。

    按理说每次 run 函数执行完,task 就没有在 context 里了,应该要被 GC 销毁,销毁的时候顺带执行资源释放。然而实际运行中,临时资源没有被释放,或者马上释放。往往是要等到整个程序结束后才开始释放。很显然,__del__没有马上被执行。

    如果把 main 放到新的子进程里跑,临时文件就更不可控了。 用“destructor”来释放资源的原因是,我无法保证使用者会记得主动去 call 释放的函数。 有没有大神来答疑解惑一下,这种资源释放如何解决。

    网上很多讨论,似乎也没有办法?就算是用 weakref ,也没有解决问题: https://docs.python.org/3.6/library/weakref.html#comparing-finalizers-with-del-methods


    ```
    class Task:
    def __init__(self):
    self._resource = create_local_files()

    def __del__(self):
    self._resource.cleanup()

    def run():
    task = Task()
    ....

    def main():
    while True:
    try:
    run()
    except:
    pass
    time.sleep(60)
    ```
    20 条回复    2023-10-08 12:42:16 +08:00
    t133
        1
    t133  
       2023-10-06 12:30:00 +08:00 via iPhone
    使用 with tempfile 这种试试?
    aijam
        2
    aijam  
       2023-10-06 12:34:08 +08:00
    +1 用 with
    ysc3839
        3
    ysc3839  
       2023-10-06 12:39:09 +08:00 via Android
    gc 语言就是不确定释放时机的,因此不能用 C++的 RAII 这种写法,只能用语言提供的 with 等机制。
    vituralfuture
        4
    vituralfuture  
       2023-10-06 12:42:58 +08:00
    GC 是无法确定时机的,不一定是立刻释放,往往是内存占用达到阈值了或者满足其他条件了才会开始 GC

    参考 https://docs.python.org/3/library/gc.html ,可以看看 python 提供的 GC 接口,可以调试 GC ,也可以立刻调用 GC

    另外还可以把清理资源这些操作放到 run 函数末尾
    BBCCBB
        5
    BBCCBB  
       2023-10-06 12:57:15 +08:00
    使用者必须得保证正确释放, 不然就没法完了. 文件, io 流都是这样的..
    直接用 with/try finally.
    julyclyde
        6
    julyclyde  
       2023-10-06 16:21:43 +08:00
    run 函数末尾试试 del task
    ch2
        7
    ch2  
       2023-10-06 19:04:56 +08:00
    暴露一个 cleanup 函数出去吧
    nowheremanx
        8
    nowheremanx  
    OP
       2023-10-07 06:43:05 +08:00
    @julyclyde del task 也不一定有用,因为 GC 并不会立即释放。而且 python 中各种引用太复杂了,可能无法保证 reference 为 0 。 最令我震惊的是,程序结束之后,有些文件也不会释放。
    nowheremanx
        9
    nowheremanx  
    OP
       2023-10-07 06:44:53 +08:00
    @ch2 是的,只能要求用户手动 cleanup 。 如果没有 cleanup 直接结束的话,扔一个 warning 好了。这是目前想到的方案,类似于 http 客户端或者数据库客户端的 behaviour 。
    dayeye2006199
        10
    dayeye2006199  
       2023-10-07 07:48:18 +08:00 via Android
    弄成一个 contextmanger ,清理逻辑显式写在里面
    nowheremanx
        11
    nowheremanx  
    OP
       2023-10-07 09:02:05 +08:00
    @dayeye2006199 这个是正要加的 feature ,但是我个人不是很喜欢 with ,代码块都要右移,所以最早的时候用__del__帮忙解决资源回收。这才踩坑了
    Maerd
        12
    Maerd  
       2023-10-07 11:38:25 +08:00
    我很好奇为什么你认为让 gc 来“帮”你清除文件是合理的,并且还预想程序崩溃时也能自动清理,这在任何语言中都是不可靠的操作,对于文件操作我们应该手动去处理。
    一般来说,python 中的临时文件都是使用 tempfile 模块来管理,tempfile 会自动在程序退出的时候删除文件
    julyclyde
        13
    julyclyde  
       2023-10-07 12:18:53 +08:00
    @nowheremanx gc 不 *立即* 释放,是很可以理解的。手工 del 可以“提醒”在可做可不做的情况下,让他去做。但至于说引用数没有降到 0 ,那可能是你自己需要解决的问题了

    至于程序结束后还没释放……可能是 bug ??
    julyclyde
        14
    julyclyde  
       2023-10-07 12:19:21 +08:00
    @nowheremanx 哈哈哈代码块右移,好像是 python 被人诟病的问题之一,就是随便加点啥就右移很远了
    julyclyde
        15
    julyclyde  
       2023-10-07 12:21:18 +08:00
    @Maerd tempfile 的自动清理工作也是靠 context manager 的。并不是在程序退出的时候执行
    Maerd
        16
    Maerd  
       2023-10-07 15:05:21 +08:00
    @julyclyde 哦这点是我说错了,tempfile 的清理是 context manager 结束或者关闭临时文件对象后会删除,这样免除了手动删除一次的问题
    julyclyde
        17
    julyclyde  
       2023-10-07 17:31:48 +08:00
    @Maerd 还有在文件关闭时删除的功能吗?怎么实现的?
    bianhui
        18
    bianhui  
       2023-10-08 09:20:05 +08:00
    首先,系统 gc 就不应该要去尝试控制。提供一个 clean 方法或者上下文管理器就行了,别老想着 pua 别的程序员,open 函数也不一定有人全部记得 close 。不会是所有的事情都要拦在自己身上。别人写不好是别人的事,就算是上下文管理器,别人也可以直接通过__enter__去调用。把自己的 api 构建好就行了。
    Maerd
        19
    Maerd  
       2023-10-08 10:30:00 +08:00
    @julyclyde 如果文件对象是 fp ,使用 fp.close()会关闭并自动清理文件
    julyclyde
        20
    julyclyde  
       2023-10-08 12:42:16 +08:00
    @Maerd Under Unix, the directory entry for the file is either not created at all or is removed immediately after the file is created. Other platforms do not support this; your code should not rely on a temporary file created using this function having or not having a visible name in the file system.
    大概是 deleted immediately 的方法。学习了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     827 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 20:15 PVG 04:15 LAX 12:15 JFK 15:15
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86