Python tempfile模块与线程不兼容;我哪里出错了?

6 投票
5 回答
2840 浏览
提问于 2025-04-15 16:58

我在使用Python的线程和临时文件模块时遇到了一个有趣的问题。似乎有些东西在线程退出之前没有被清理干净,而我又碰到了打开文件数量的限制。(这是在OS X 10.5.8上,使用Python 2.5.1。)

不过,如果我自己模仿一下临时文件模块的做法(虽然没有进行所有的安全检查,但只是生成一个文件描述符,然后用os.fdopen来创建一个文件对象),我就没有问题。

在我把这个问题提交给Python作为一个bug之前,我想先在这里问问,因为我更可能是哪里做错了。但如果真是这样,经过一天的尝试,我还是没找到原因。

#!/usr/bin/python

import threading
import thread
import tempfile
import os
import time
import sys

NUM_THREADS = 10000

def worker_tempfile():
    tempfd, tempfn = tempfile.mkstemp()
    tempobj = os.fdopen(tempfd, 'wb')
    tempobj.write('hello, world')
    tempobj.close()
    os.remove(tempfn)
    time.sleep(10)

def worker_notempfile(index):
    tempfn = str(index) + '.txt'
    # The values I'm passing os.open may be different than tempfile.mkstemp 
    # uses, but it works this way as does using the open() function to create
    # a file object directly.
    tempfd = os.open(tempfn, 
                     os.O_EXCL | os.O_CREAT | os.O_TRUNC | os.O_RDWR)
    tempobj = os.fdopen(tempfd, 'wb')
    tempobj.write('hello, world')
    tempobj.close()
    os.remove(tempfn)
    time.sleep(10)

def main():
    for count in range(NUM_THREADS):
        if count % 100 == 0:
            print('Opening thread %s' % count)
        wthread = threading.Thread(target=worker_tempfile)
        #wthread = threading.Thread(target=worker_notempfile, args=(count,))
        started = False
        while not started:
            try:
                wthread.start()
                started = True
            except thread.error:
                print('failed starting thread %s; sleeping' % count)
                time.sleep(3)

if __name__ == '__main__':
    main()

如果我运行时激活了worker_notempfile这一行,而把worker_tempfile这一行注释掉,它就能正常完成。

反过来(使用worker_tempfile),我就会遇到以下错误:

$ python threadtempfiletest.py 
Opening thread 0
Opening thread 100
Opening thread 200
Opening thread 300
Exception in thread Thread-301:
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/threading.py", line 460, in __bootstrap
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/threading.py", line 440, in run
  File "threadtempfiletest.py", line 17, in worker_tempfile
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/tempfile.py", line 302, in mkstemp
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/tempfile.py", line 236, in _mkstemp_inner
OSError: [Errno 24] Too many open files: '/var/folders/4L/4LtD6bCvEoipksvnAcJ2Ok+++Tk/-Tmp-/tmpJ6wjV0'

有没有人知道我哪里做错了?这是Python的bug,还是我太笨了?

更新 2009-12-14: 我想我找到了答案,但我不喜欢这个结果。因为没有人能重现这个问题,我开始在办公室里找机器。除了我的机器,其他的都能通过测试。我在一台使用相同软件版本的Mac上测试过,甚至还找了一台硬件和软件配置完全相同的Desktop G5——结果也是一样。两个测试(使用临时文件和不使用临时文件)在所有机器上都成功了。

为了好玩,我下载了Python 2.6.4,在我的桌面上试了一下,结果和Python 2.5.1一样:临时文件失败,不使用临时文件成功。

这让我得出结论,我的Mac上可能出了问题,但我实在搞不清楚是什么。欢迎任何建议。

5 个回答

3

我觉得你的问题可以在这里找到答案。你需要明确地使用os.close()来关闭mkstemp返回的元组中的第一个部分,也就是文件描述符。

补充说明:不,提问者已经在做应该做的事情了。我把这个回答保留在这里是为了那个不错的链接。

4

我在Mac OS X 10.5.9上用苹果自己提供的Python 2.5.1测试时,没有遇到任何问题,运行得很好!

我在一台Macbook Pro(用的是Intel处理器)和一台老旧的PowerMac(用的是PPC处理器)上都试过了。

所以我猜可能是10.5.8版本里有个我没注意到的bug(我手头没有10.5.8的系统可以测试,因为每次软件更新我都会及时升级)。我建议你试试升级到10.5.9,看看问题是否消失。如果问题依然存在,我就不知道为什么我的机器和你的机器会有这样的不同表现了。

0

因为没有人能重现这个问题,所以我开始在办公室里找机器测试。结果除了我的机器,其他的都能正常运行。我还在一台和我用的版本完全一样的Mac上测试过,结果也是一样。我甚至找了一台硬件和软件配置完全相同的Desktop G5,结果也是一样。无论是用tempfile还是不用tempfile,其他机器上的测试都成功了。

为了好玩,我下载了Python 2.6.4,在我的桌面上试了一下,结果和Python 2.5.1一样:用tempfile失败,不用tempfile成功。

这让我得出一个结论:我的Mac上可能有什么问题,所以这应该不是其他人会遇到的问题。

非常感谢所有帮助过我的人(特别是Alex Martelli)!

撰写回答