2009-12-13 11 views
6

Mam interesujący problem z wątkami i modułem tempfile w Pythonie. Coś wygląda na to, że nie zostanie oczyszczone, dopóki wątki nie znikną, a ja działam na otwartym limicie plików. (To jest na OS X 10.5.8, Python 2.5.1.)Python tempfile moduł i wątki nie grają ładnie; Co ja robię źle?

Ale jeśli w pewnym sensie replikuję to, co robi moduł tempfile (nie wszystkie kontrole bezpieczeństwa, ale po prostu generowanie deskryptora pliku, a następnie używanie os. fdopen do utworzenia obiektu pliku) Nie mam żadnych problemów.

Zanim złożyłem to jako błąd w Pythonie, pomyślałem, że sprawdzę tutaj, ponieważ jest bardziej prawdopodobne, że robię coś subtelnie nie tak. Ale jeśli tak, to dzień, w którym próbowałem to rozgryźć, nigdzie mnie nie dostał.

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

Jeśli uruchomić go z worker_notempfile linia aktywnym i worker_tempfile linii zakomentowanych, biegnie do końca.

Innym sposobem wokół (używając worker_tempfile) pojawia się następujący błąd:

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

jakieś pomysły, co robię źle? Czy jest to błąd w Pythonie, czy też jestem zuchwały?

AKTUALIZACJA 2009-12-14: Myślę, że znalazłem odpowiedź, ale nie podoba mi się to. Ponieważ nikt nie był w stanie powtórzyć problemu, zacząłem polować w naszym biurze na maszyny. Przekazał wszystko poza moją maszyną. Testowałem na komputerze Mac z tymi samymi wersjami oprogramowania, których używałem. Poszedłem nawet na polowanie na komputer stacjonarny G5 z konfiguracją tego samego sprzętu i oprogramowania, którą miałem - taki sam wynik. Oba testy (z tempfile i bez tempfile) zakończyły się pomyślnie.

W przypadku kopnięć pobrałem wersję Python 2.6.4 i wypróbowałem ją na moim komputerze, i ten sam wzorzec w moim systemie, ponieważ Python 2.5.1: tempfile nie powiodło się, a notempfile powiodło się.

Prowadzi mnie to do wniosku, że coś dzieje się na moim Macu, ale na pewno nie wiem, co. Wszelkie sugestie są mile widziane.

+1

Nie można pomóc, ale +1 dla jasnego pytania z dobrym kodem. –

+0

Czy możesz podać wersję Pythona? Nie wiem, czy to ma znaczenie, ale może. –

+0

Jonathan: Wielkie dzięki! Peter: Python 2.5.1. Zmieniłem też to pytanie, aby to odzwierciedlić. – Schof

Odpowiedz

0

Ponieważ nikt nie był w stanie powtórzyć problemu, poszedłem polować po naszym biurze na maszyny. Przekazał wszystko poza moją maszyną. Testowałem na komputerze Mac z tymi samymi wersjami oprogramowania, których używałem. Poszedłem nawet na polowanie na komputer stacjonarny G5 z konfiguracją tego samego sprzętu i oprogramowania, którą miałem - taki sam wynik. Oba testy (z tempfile i bez tempfile) zakończyły się pomyślnie.

W przypadku kopnięć pobrałem wersję Python 2.6.4 i wypróbowałem ją na moim komputerze, i ten sam wzorzec w moim systemie, ponieważ Python 2.5.1: tempfile nie powiodło się, a notempfile powiodło się.

Prowadzi mnie to do wniosku, że coś zostało uszkodzone na moim Macu, więc nie jest to prawdopodobnie problem, z którym nikt inny się nie spotka.

Dziękuję bardzo wszystkim (zwłaszcza Alexowi Martelli), którzy pomogli w tym!

3

Myślę, że odpowiedź można znaleźć here. Musisz jednoznacznie podać kod podany jako pierwsza część krotki, którą daje ci mkstemp.

Edycja: nie, OP robi już to, co ma być zrobione. Zostawiam odpowiedź na ładny link.

+1

Ale ten post mówi "Funkcja os.fdopen (fd) zwróci obiekt pliku Pythona używający tego samego deskryptora pliku Zamknięcie tego obiektu pliku spowoduje zamknięcie deskryptora pliku na poziomie OS "- co jest (lub powinno być według mojej najlepszej wiedzy) poprawne i dlatego błąd OP jest tak tajemniczy ... on ** używa ** 'fdopen', a następnie zamyka obiekt pliku ... a mimo to przecieka z deskryptorów plików, co jest poważną tajemnicą! –

+0

D'oh! Dzięki za poprawkę. Zostawię tę odpowiedź , tylko dlatego, że zasób, do którego się prowadzi, jest przydatny –

+0

Zdaję sobie sprawę, że ten wpis jest bardzo stary, ale wyłączył mnie z podobnego problemu: Jest to dość nieintuicyjne w przypadku błędów. Można by pomyśleć, że gdy deskryptor pliku wrócił z '' mkstemp () '' wykracza poza zakres, wszystko by się posprzątało po sobie ... ale na niektórych maszynach nie, dopóki pętla równoległości się nie skończy. Poniższy przykład pokazuje dobry sposób na oszukiwanie komputera, który myśli, że jego dysk jest pełny, gdy tak nie jest. Pliki * są * faktycznie niszczone po drodze, ale system operacyjny nie zdaje sobie z tego sprawy, dopóki program nie zostanie zamknięty. https: //gist.github.com/ejhumphrey/b556c8be89fb6d5aeed1 –

4

Nie mogę odtworzyć problemu (własna kompilacja Apple) Python 2.5.1 na Mac OS X 10.5.9 - przebiega do końca dobrze!

Próbowałem już zarówno na Macbook Pro, tj. Procesorze Intela, jak i starym PowerMacu, tj. Procesorze PPC.

Mogę sobie tylko wyobrazić, że w 10.5.8 wystąpił błąd, którego nigdy nie zauważyłem (nie mam żadnego 10.5.8 do przetestowania, ponieważ zawsze uaktualniam go natychmiast, gdy aktualizacja oprogramowania go oferuje). Wszystko, co mogę zasugerować, to to, że próbujesz uaktualnić do wersji 10.5.9 i sprawdzić, czy błąd zniknie - jeśli nie, nie mam pojęcia, jak ta różnica w zachowaniu między moimi maszynami a twoją jest możliwa.

+0

Hmm. Wydaje mi się, że 10.5.8 jest najnowszą aktualizacją oprogramowania. Być może jest to sprawa PowerPC kontra Intel? (Jestem na PowerPC.) – Schof

+0

Nie zawiedzie dla mnie na 10.5.8 PPC z Apple 2.5.1. –

+0

10.5.8 * jest * najnowszą wersją wymienioną na stronie Apple. Czy wersja 10.5.9 to wersja przedpremierowa? – Schof

1

Właśnie przetestowałem twój kod na moim komputerze z Ubuntu Linux i zadziałało idealnie.

Mam jedną sugestię do wypróbowania. Nie wiem, czy to pomoże, ale nie zaszkodzi. Przepisz kod do korzystania z:

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) 

Oświadczenie with powinien upewnić się, że obiekt zostanie zamknięty plik nie wiem co. Być może to może pomóc?

Powodzenia. Świetna robota na ten temat, nawiasem mówiąc.

1

Dlaczego uważasz, że ten błąd nie jest prawdziwy? Uruchamiasz 10000 wątków, z których każdy otwiera plik, podczas gdy maksymalna liczba otwartych plików wynosi zazwyczaj 1024 w systemach uniksowych.

Najpierw spróbuj ręcznie śledzić liczbę obecnie otwieranych plików i sprawdź, czy przekracza limit systemu operacyjnego.

+0

Powodem, dla którego myślę, że to może być błąd w języku Python (lub błąd w moim kodzie), jest to, że jedna funkcja zawodzi (plik_problemu_pracy), podczas gdy z grubsza odpowiada funkcji (plik_umyślny_pracy). – Schof

+0

To bardzo słaby powód. Wywoływanie różnych funkcji (wbudowanych funkcji os. * Lub funkcji tempfile napisanych w języku Python. *) Z różnymi implementacjami może mieć duży wpływ na sposób parowania. Dlatego sugeruję, abyś sprawdził, czy błąd nie jest prawdziwy. –

Powiązane problemy