Python tempfile模块和线程不是很好玩;我究竟做错了什么?

时间:2009-12-13 02:03:24

标签: python multithreading temporary-files

我遇到了一个有趣的线程问题和Python中的tempfile模块。在线程退出之前,某些东西似乎没有得到清理,而且我正在针对打开的文件限制运行。 (这是在OS X 10.5.8,Python 2.5.1上。)

然而,如果我复制tempfile模块正在做什么(不是所有的安全检查,而只是生成文件描述符,然后使用os.fdopen来生成文件对象),我没有问题。

在将此作为Python的错误提交之前,我想我会在这里查看,因为我更有可能做了一些巧妙的错误。但是,如果我是这样,那一天试图解决这个问题并没有把我带到任何地方。

#!/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中的一个错误,还是我头脑发热?

更新2009-12-14: 我想我找到了答案,但我不喜欢它。由于没有人能够复制这个问题,我去办公室寻找机器。除了我的机器,它传递了一切。我在Mac上使用与我使用的相同软件版本进行了测试。我甚至用我所拥有的完全相同的硬件和软件配置来寻找桌面G5 - 结果相同。两个测试(使用tempfile和没有tempfile)都成功了。

对于踢,我下载了Python 2.6.4,并在我的桌面上尝试了它,我的系统上的模式与Python 2.5.1相同:tempfile失败,并且notempfile成功。

这让我得出的结论是我的Mac上出现了一些东西,但我肯定无法弄清楚是什么。欢迎任何建议。

5 个答案:

答案 0 :(得分:4)

我无法在Mac OS X 10.5.9上重现(Apple自己构建的)Python 2.5.1的问题 - 运行完成就好了!

我已经在Macbook Pro(即Intel处理器)和旧的PowerMac(即PPC处理器)上尝试过。

所以我只能想象10.5.8中一定有一个我从未注意到的错误(没有任何10.5.8可以测试,因为我总是在软件更新提供的时候立即升级)。我可以建议你尝试升级到10.5.9并查看错误是否消失 - 如果没有,我不知道我的机器和你的机器之间的这种行为差异是可能的。

答案 1 :(得分:3)

我认为你的答案可以找到here。您必须明确os.close()作为mkstemp为您提供的元组的第一部分给出的文件描述符。

编辑:不,OP已经在做应该做的事了。我正在为这个很好的链接留下答案。

答案 2 :(得分:1)

我刚刚在我的Ubuntu Linux计算机上测试了你的代码,它对我来说非常适合。

我有一个建议让你试试。我不知道它会有所帮助,但它不会伤害。重写您的代码以用于:

from __future__ import with_statement

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

with语句应该确保文件对象无论如何都会被关闭。也许它可能会有所帮助?

祝你好运。顺便说一下,这个问题很棒。

答案 3 :(得分:1)

为什么你认为错误不是真的?您正在启动10000个线程,每个线程都打开一个文件,而Unix系统下打开的最大文件数通常是1024个。

首先尝试手动跟踪当前打开的文件数量,并检查它是否超过操作系统限制。

答案 4 :(得分:0)

由于没有人能够复制这个问题,我去办公室寻找机器。除了我的机器,它传递了一切。我在Mac上使用与我使用的相同软件版本进行了测试。我甚至用我所拥有的完全相同的硬件和软件配置来寻找桌面G5 - 结果相同。两个测试(使用tempfile和没有tempfile)都成功了。

对于踢,我下载了Python 2.6.4,并在我的桌面上尝试了它,我的系统上的模式与Python 2.5.1相同:tempfile失败,并且notempfile成功。

这让我得出的结论是我的Mac上出现了一些东西,所以这不可能成为其他任何人遇到过的问题。

非常感谢所有人(特别是Alex Martelli)为此提供了帮助!