2009-12-13 13 views
6

Sto riscontrando un problema interessante con i thread e il modulo tempfile in Python. Sembra che non si stia pulendo qualcosa fino all'uscita dai thread e sto correndo contro un limite di file aperti. (Questo è su OS X 10.5.8, Python 2.5.1.)Python tempfile module e thread non stanno giocando bene; Che cosa sto facendo di sbagliato?

Tuttavia, se riesco a replicare ciò che sta facendo il modulo tempfile (non tutti i controlli di sicurezza, ma solo la generazione di un descrittore di file e quindi l'utilizzo di os. fdopen per produrre un oggetto file) Non ho problemi.

Prima di archiviare questo come un bug con Python, ho pensato di controllare qui, poiché è molto più probabile che sto facendo qualcosa di leggermente sbagliato. Ma se lo sono, un giorno in cui provo a capirlo non mi ha portato da nessuna parte.

#!/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() 

Se l'eseguo con la worker_notempfile linea attiva e la linea di worker_tempfile commentato-out, si corre a compimento.

Il contrario (utilizzando worker_tempfile) ottengo il seguente errore:

$ 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' 

Tutte le idee che sto facendo male? Questo è un bug in Python, o sono ossessionato?

UPDATE 2009-12-14: credo di aver trovato la risposta, ma non mi piace. Dal momento che nessuno è stato in grado di replicare il problema, sono andato a caccia nel nostro ufficio per le macchine. Ha trasmesso tutto tranne la mia macchina. Ho provato su un Mac con le stesse versioni del software che stavo usando. Sono persino andato a cercare un Desktop G5 con la stessa configurazione hardware e software che avevo - lo stesso risultato. Entrambi i test (con tempfile e senza tempfile) sono riusciti su tutto.

Per kick ho scaricato Python 2.6.4 e l'ho provato sul mio desktop e lo stesso pattern sul mio sistema come Python 2.5.1: tempfile non riuscito e notempfile riuscito.

Questo mi sta portando alla conclusione che qualcosa è nascosto sul mio Mac, ma di certo non riesco a capire cosa. Qualsiasi suggerimento è benvenuto.

+1

Impossibile aiutare, ma +1 per una domanda chiara con un buon codice. –

+0

Puoi darci la versione di Python, per favore? Non so se è importante, ma potrebbe. –

+0

Jonathan: Grazie mille! Peter: Python 2.5.1. Ho anche modificato la domanda per riflettere questo. – Schof

risposta

0

Dal momento che nessuno è stato in grado di replicare il problema, sono andato a caccia nel nostro ufficio per le macchine. Ha trasmesso tutto tranne la mia macchina. Ho provato su un Mac con le stesse versioni del software che stavo usando. Sono persino andato a cercare un Desktop G5 con la stessa configurazione hardware e software che avevo - lo stesso risultato. Entrambi i test (con tempfile e senza tempfile) sono riusciti su tutto.

Per kick ho scaricato Python 2.6.4 e l'ho provato sul mio desktop e lo stesso pattern sul mio sistema come Python 2.5.1: tempfile non riuscito e notempfile riuscito.

Questo mi porta alla conclusione che qualcosa è nascosto sul mio Mac, quindi non è probabile che questo sia un problema che qualcun altro incontra mai.

Grazie MOLTO a tutti (specialmente Alex Martelli) che ci hanno aiutato!

3

Penso che la tua risposta possa essere trovata here. Devi esplicitamente os.close() il descrittore di file indicato come la prima parte della tupla fornita da mkstemp.

Modifica: no, l'OP sta già facendo ciò che dovrebbe essere fatto. Sto lasciando la risposta per il link piacevole.

+1

Ma quel post dice "La funzione os.fdopen (fd) tornerà un oggetto file Python che utilizza lo stesso descrittore di file. La chiusura di quell'oggetto file chiuderà il descrittore di file a livello di sistema operativo "- che è (o dovrebbe essere il meglio delle mie conoscenze) corretto ed è il motivo per cui l'errore dell'OP è così misterioso ... lui ** sta ** usando 'fdopen' e poi chiude l'oggetto file ... eppure sta perdendo comunque i descrittori di file, che è un mistero serio! –

+0

D'oh! Grazie per la correzione. Lascerò questa risposta , solo perché la risorsa a cui si collega è utile. –

+0

Mi rendo conto che questo post è molto vecchio, ma mi ha solo salvato su un problema simile. Questo è piuttosto intuitivo come bug andare. Si potrebbe pensare che quando il descrittore di file restituito da '' mkstemp () '' esce dal campo di applicazione, tutto si ripulirebbe da solo ... ma su alcune macchine, no, non prima che il ciclo di parallelizzazione finisca. L'esempio seguente mostra un buon modo per indurre la tua macchina a pensare che il suo disco sia pieno quando non lo è. I file * sono * effettivamente distrutti lungo il percorso, ma il sistema operativo non se ne rende conto fino alla chiusura del programma. https: //gist.github.com/ejhumphrey/b556c8be89fb6d5aeed1 –

4

Non riesco a riprodurre il problema con (la stessa build di Apple) Python 2.5.1 su Mac OS X 10.5.9 - viene eseguito fino al completamento!

Ho provato entrambi su un Macbook Pro, cioè un processore Intel e un vecchio PowerMac, cioè un processore PPC.

Quindi posso solo immaginare che ci sia stato un errore in 10.5.8 che non ho mai notato (non ho alcun 10.5.8 in giro da testare, poiché mi aggiorno sempre prontamente ogni volta che l'aggiornamento del software lo offre). Tutto quello che posso suggerire è che si prova ad aggiornare a 10.5.9 e vedere se il bug scompare - se non lo fa, non ho idea di come sia possibile questa differenza di comportamento tra le mie macchine e la tua.

+0

Hmm. 10.5.8 sembra essere l'ultima versione dell'aggiornamento software che mi darò. Forse questa è una cosa PowerPC vs Intel? (Sono su PowerPC.) – Schof

+0

Non fallisce per me su 10.5.8 PPC con Apple 2.5.1. –

+0

10.5.8 * * è l'ultima versione elencata sul sito Web di Apple. 10.5.9 è una versione preliminare? – Schof

1

Ho appena testato il codice sul mio computer Linux Ubuntu qui, e ha funzionato perfettamente per me.

Ho un suggerimento da provare. Non so che aiuterà ma non può far male. Riscrivere il codice da utilizzare con:

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) 

La dichiarazione with dovrebbe fare in modo che l'oggetto file viene chiusa non importa quale. Forse potrebbe essere d'aiuto?

Buona fortuna. A proposito, bel lavoro sulla domanda.

1

Perché pensi che l'errore non sia autentico? Stai lanciando 10.000 thread, ognuno aprendo un file, mentre il numero massimo di file aperti è in genere 1024 sotto sistemi Unix.

Prima prova a tenere traccia manualmente del numero di file attualmente aperti e controlla se supera il limite del sistema operativo.

+0

La ragione per cui penso che questo potrebbe essere un bug Python (o un errore nel mio codice) è perché una funzione non riesce (worker_tempfile) mentre una funzione approssimativamente equivalente ha successo (worker_notempfile). – Schof

+0

È una ragione molto debole. Chiamare diverse funzioni (le funzioni built-in. * O il tempfile scritto da Python. * Funzioni) con diverse implementazioni può avere un grande impatto sul modo in cui le cose vengono parallelizzate. Ecco perché ti suggerisco di verificare che l'errore non sia autentico. –

Problemi correlati